diff --git a/.all-contributorsrc b/.all-contributorsrc index ced3b155b..a2d13c2fc 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,6 +1,7 @@ { "files": [ - "website/src/pages/credits.mdx" + "website/src/pages/credits.mdx", + "docs/src/assets/contributors.html" ], "imageSize": 75, "commit": false, diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 000000000..5edf609fa --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,71 @@ +language: en-US +tone_instructions: '' +early_access: false +enable_free_tier: true +reviews: + profile: chill + request_changes_workflow: false + high_level_summary: true + high_level_summary_placeholder: '@coderabbitai summary' + auto_title_placeholder: '@coderabbitai' + review_status: true + poem: true + collapse_walkthrough: false + sequence_diagrams: true + path_filters: [] + path_instructions: [] + abort_on_close: true + auto_review: + enabled: true + auto_incremental_review: true + ignore_title_keywords: [] + labels: [] + drafts: false + base_branches: ['v3-alpha', 'master'] + tools: + shellcheck: + enabled: true + ruff: + enabled: true + markdownlint: + enabled: true + github-checks: + enabled: true + timeout_ms: 90000 + languagetool: + enabled: true + enabled_only: false + level: default + biome: + enabled: true + hadolint: + enabled: true + swiftlint: + enabled: true + phpstan: + enabled: true + level: default + golangci-lint: + enabled: true + yamllint: + enabled: true + gitleaks: + enabled: true + checkov: + enabled: true + detekt: + enabled: true + eslint: + enabled: true +chat: + auto_reply: true +knowledge_base: + opt_out: false + learnings: + scope: auto + issues: + scope: auto + jira: + project_keys: [] + linear: + team_keys: [] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 84b7cb6dc..c8ace9747 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -7,6 +7,8 @@ body: - type: markdown attributes: value: | + ***Please note: No bug reports are currently being accepted for Wails v3*** + ***Please note: No bug reports are currently being accepted for Wails v3*** ***Please note: No bug reports are currently being accepted for Wails v3*** Before submitting this issue, please do the following: - Do a web search for your error. This usually leads to a much better understanding of the issue. diff --git a/.github/file-labeler.yml b/.github/file-labeler.yml new file mode 100644 index 000000000..69494cbae --- /dev/null +++ b/.github/file-labeler.yml @@ -0,0 +1,44 @@ +# File path specific labels +v2-only: + - 'v2/**/*' + +v3-alpha: + - 'v3/**/*' + +windows: + - '**/*_windows.go' + - 'v2/internal/frontend/desktop/windows/**/*' + +macos: + - '**/*_darwin.go' + - 'v2/internal/frontend/desktop/darwin/**/*' + +linux: + - '**/*_linux.go' + - 'v2/internal/frontend/desktop/linux/**/*' + +cli: + - 'v2/cmd/**/*' + - 'v3/cmd/**/*' + - '**/cli/**/*' + - '**/commands/**/*' + +documentation: + - '**/*.md' + - 'docs/**/*' + - 'website/**/*' + - 'mkdocs-website/**/*' + +templates: + - '**/templates/**/*' + - '**/template/**/*' + +runtime: + - '**/runtime/**/*' + - 'v2/internal/runtime/**/*' + - 'v3/internal/runtime/**/*' + +bindings: + - 'v2/internal/binding/**/*' + - 'v3/internal/generator/**/*' + diff --git a/.github/issue-labeler.yml b/.github/issue-labeler.yml new file mode 100644 index 000000000..0a7949051 --- /dev/null +++ b/.github/issue-labeler.yml @@ -0,0 +1,144 @@ +# Version labels +v2-only: + - '\[v2\]' + - '\(v2\)' + - 'v2:' + - 'version 2' + - 'wails v2' + - 'using v2' + - 'master branch' + +v3-alpha: + - '\[v3\]' + - '\(v3\)' + - 'v3:' + - '\[v3-alpha\]' + - '\(v3-alpha\)' + - 'version 3' + - 'wails v3' + - 'using v3' + - 'v3-alpha branch' + +# Component labels +webview2: + - 'webview2' + - 'windows' + - 'microsoft edge' + - 'edge browser' + - 'IE' + - 'Explorer' + - 'browser crashes' + +macos: + - 'macOS' + - 'mac OS' + - 'OS X' + - 'darwin' + - 'cocoa' + - 'Safari' + - 'Catalyst' + - 'Ventura' + - 'Sonoma' + - 'apple' + +linux: + - 'linux' + - 'ubuntu' + - 'debian' + - 'fedora' + - 'gtk' + - 'webkitgtk' + - 'webkit2gtk' + - 'gnome' + - 'x11' + - 'wayland' + +cli: + - 'cli' + - 'command line' + - 'wails doctor' + - 'wails init' + - 'wails build' + - 'wails dev' + - 'template' + - 'scaffolding' + +# Type labels +bug: + - 'bug' + - 'crash' + - 'broken' + - 'failure' + - 'error' + - 'failed' + - 'panic' + - 'segfault' + - 'issue' + - 'not working' + - 'problem' + +enhancement: + - 'feature' + - 'enhancement' + - 'request' + - 'add' + - 'new' + - 'improve' + - 'functionality' + - 'support for' + - 'please add' + - 'would be nice' + +documentation: + - 'docs' + - 'documentation' + - 'readme' + - 'example' + - 'tutorial' + - 'guide' + - 'explanation' + - 'clarification' + - 'instructions' + +security: + - 'security' + - 'vulnerability' + - 'exploit' + - 'hack' + - 'CVE' + - 'secure' + - 'encryption' + - 'hardening' + +performance: + - 'performance' + - 'slow' + - 'speed' + - 'memory leak' + - 'cpu usage' + - 'high memory' + - 'lag' + - 'freeze' + - 'optimization' + +# Priority labels +high-priority: + - 'urgent' + - 'critical' + - 'security' + - 'high priority' + - 'important' + - 'production' + - 'blocker' + - 'blocking' + +question: + - 'how to' + - 'how do i' + - 'can I' + - 'is it possible' + - 'question' + - 'help me' + - 'need help' + - 'assistance' + - 'confused' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bf3d8de39..9f8d049ba 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,9 +1,20 @@ # Description @@ -14,7 +25,7 @@ Fixes # (issue) ## Type of change -Please delete options that are not relevant. +Please select the option that is relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) @@ -28,6 +39,8 @@ Please describe the tests that you ran to verify your changes. Provide instructi - [ ] Windows - [ ] macOS - [ ] Linux + +If you checked Linux, please specify the distro and version. ## Test Configuration @@ -35,7 +48,7 @@ Please paste the output of `wails doctor`. If you are unable to run this command # Checklist: -- [ ] I have updated `website/src/pages/changelog.mdx` with details of this PR +- [ ] I have updated `v3/UNRELEASED_CHANGELOG.md` with details of this PR - [ ] My code follows the general coding style of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas diff --git a/.github/stale.yml b/.github/stale.yml index 805bd589d..d8bcc83ec 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Number of days of inactivity before an issue becomes stale -daysUntilStale: 30 +daysUntilStale: 45 # Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 +daysUntilClose: 10 # Issues with these labels will never be considered stale exemptLabels: - pinned @@ -9,14 +9,28 @@ exemptLabels: - onhold - inprogress - "Selected For Development" + - bug + - enhancement + - v3-alpha + - high-priority # Label to use when marking an issue as stale -staleLabel: "Wont Fix" +staleLabel: "stale" # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. + recent activity. It will be closed if no further activity occurs within the next 10 days. + + If this issue is still relevant, please add a comment to keep it open. + Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false +closeComment: > + This issue has been automatically closed due to lack of activity. + Please feel free to reopen it if it's still relevant. exemptMilestones: true exemptAssignees: true +# Only mark issues (not PRs) +only: issues +# Exempt issues created before a certain date +exemptCreatedBefore: "2024-01-01T00:00:00Z" +# Starts checking issues only after the specified date +startDate: "2025-06-01T00:00:00Z" diff --git a/.github/workflows/auto-label-issues.yml b/.github/workflows/auto-label-issues.yml new file mode 100644 index 000000000..097eba533 --- /dev/null +++ b/.github/workflows/auto-label-issues.yml @@ -0,0 +1,33 @@ +name: Auto Label Issues + +on: + issues: + types: [opened, edited, reopened] + pull_request_target: + types: [opened, edited, reopened, synchronize] + +jobs: + auto-label: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Label issues and PRs by content + uses: github/issue-labeler@v3.4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + configuration-path: .github/issue-labeler.yml + enable-versioned-regex: 0 + include-title: 1 + + - name: Label issues and PRs by file paths + uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + configuration-path: .github/file-labeler.yml + sync-labels: true diff --git a/.github/workflows/automated-releases.yml b/.github/workflows/automated-releases.yml new file mode 100644 index 000000000..e88f54eb9 --- /dev/null +++ b/.github/workflows/automated-releases.yml @@ -0,0 +1,372 @@ +name: Automated Nightly Releases + +on: + workflow_dispatch: + inputs: + force_release: + description: 'Force release even if no changes detected' + required: false + default: false + type: boolean + dry_run: + description: 'Run in dry-run mode (no actual releases)' + required: false + default: false + type: boolean + # schedule: + # Run at 2 AM UTC every day - DISABLED for safety until ready + # - cron: '0 2 * * *' + +env: + GO_VERSION: '1.24' + +jobs: + check-permissions: + name: Check Release Permissions + runs-on: ubuntu-latest + outputs: + authorized: ${{ steps.check.outputs.authorized }} + steps: + - name: Check if user is authorized for releases + id: check + run: | + # Only allow specific users to trigger releases + AUTHORIZED_USERS="leaanthony" + + if [[ "$AUTHORIZED_USERS" == *"${{ github.actor }}"* ]]; then + echo "✅ User ${{ github.actor }} is authorized for releases" + echo "authorized=true" >> $GITHUB_OUTPUT + else + echo "❌ User ${{ github.actor }} is not authorized for releases" + echo "authorized=false" >> $GITHUB_OUTPUT + fi + + detect-v2-changes: + name: Detect v2 Changes + runs-on: ubuntu-latest + needs: check-permissions + if: needs.check-permissions.outputs.authorized == 'true' + outputs: + has_changes: ${{ steps.changes.outputs.has_changes }} + commits_since_last: ${{ steps.changes.outputs.commits_since_last }} + last_release_tag: ${{ steps.changes.outputs.last_release_tag }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check for v2 changes since last release + id: changes + run: | + echo "🔍 Checking for v2 changes since last release..." + + # Find the last v2 release tag + LAST_TAG=$(git tag -l "v2.*" --sort=-version:refname | head -n 1) + if [ -z "$LAST_TAG" ]; then + echo "No previous v2 tags found, assuming first release" + LAST_TAG=$(git rev-list --max-parents=0 HEAD) + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "commits_since_last=999" >> $GITHUB_OUTPUT + echo "last_release_tag=none" >> $GITHUB_OUTPUT + else + echo "Last v2 release tag: $LAST_TAG" + echo "last_release_tag=$LAST_TAG" >> $GITHUB_OUTPUT + + # Count commits since last release affecting v2 or root files + COMMITS_COUNT=$(git rev-list --count ${LAST_TAG}..HEAD -- v2/ website/ README.md CHANGELOG.md || echo "0") + echo "Commits since last v2 release: $COMMITS_COUNT" + echo "commits_since_last=$COMMITS_COUNT" >> $GITHUB_OUTPUT + + if [ "$COMMITS_COUNT" -gt 0 ] || [ "${{ github.event.inputs.force_release }}" == "true" ]; then + echo "✅ Changes detected or forced release" + echo "has_changes=true" >> $GITHUB_OUTPUT + else + echo "ℹ️ No changes detected since last release" + echo "has_changes=false" >> $GITHUB_OUTPUT + fi + fi + + detect-v3-changes: + name: Detect v3-alpha Changes + runs-on: ubuntu-latest + needs: check-permissions + if: needs.check-permissions.outputs.authorized == 'true' + outputs: + has_changes: ${{ steps.changes.outputs.has_changes }} + commits_since_last: ${{ steps.changes.outputs.commits_since_last }} + last_release_tag: ${{ steps.changes.outputs.last_release_tag }} + steps: + - name: Checkout v3-alpha branch + uses: actions/checkout@v4 + with: + ref: v3-alpha + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check for v3-alpha changes since last release + id: changes + run: | + echo "🔍 Checking for v3-alpha changes since last release..." + + # Find the last v3 alpha release tag + LAST_TAG=$(git tag -l "v3.*-alpha.*" --sort=-version:refname | head -n 1) + if [ -z "$LAST_TAG" ]; then + echo "No previous v3-alpha tags found, assuming first release" + LAST_TAG=$(git rev-list --max-parents=0 HEAD) + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "commits_since_last=999" >> $GITHUB_OUTPUT + echo "last_release_tag=none" >> $GITHUB_OUTPUT + else + echo "Last v3-alpha release tag: $LAST_TAG" + echo "last_release_tag=$LAST_TAG" >> $GITHUB_OUTPUT + + # Count commits since last release affecting v3 or docs + COMMITS_COUNT=$(git rev-list --count ${LAST_TAG}..HEAD -- v3/ docs/ || echo "0") + echo "Commits since last v3-alpha release: $COMMITS_COUNT" + echo "commits_since_last=$COMMITS_COUNT" >> $GITHUB_OUTPUT + + if [ "$COMMITS_COUNT" -gt 0 ] || [ "${{ github.event.inputs.force_release }}" == "true" ]; then + echo "✅ Changes detected or forced release" + echo "has_changes=true" >> $GITHUB_OUTPUT + else + echo "ℹ️ No changes detected since last release" + echo "has_changes=false" >> $GITHUB_OUTPUT + fi + fi + + release-v2: + name: Create v2 Release + runs-on: ubuntu-latest + needs: [check-permissions, detect-v2-changes] + if: | + needs.check-permissions.outputs.authorized == 'true' && + needs.detect-v2-changes.outputs.has_changes == 'true' + outputs: + version: ${{ steps.release.outputs.version }} + release_notes: ${{ steps.release.outputs.release_notes }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Run v2 release script and extract notes + id: release + run: | + echo "🚀 Running v2 release script..." + cd v2/tools/release + + # Run release script and capture output + RELEASE_OUTPUT=$(go run release.go 2>&1) + echo "$RELEASE_OUTPUT" + + # Extract version from output or version file + NEW_VERSION=$(cat ../../cmd/wails/internal/version.txt) + echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT + + # Extract release notes from delimited output + RELEASE_NOTES=$(echo "$RELEASE_OUTPUT" | sed -n '/=== RELEASE NOTES FOR/,/=== END RELEASE NOTES ===/p' | sed '1d;$d') + + # Save release notes to file for multiline output + echo "$RELEASE_NOTES" > ../../../release_notes_v2.md + + # Set output (escape for GitHub Actions) + { + echo "release_notes<> $GITHUB_OUTPUT + + echo "✅ v2 release script completed - version: $NEW_VERSION" + + - name: Create v2 git tag and release + if: github.event.inputs.dry_run != 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ steps.release.outputs.version }}" + echo "📝 Creating v2 release: $VERSION" + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Commit the changelog changes + git add website/src/pages/changelog.mdx v2/cmd/wails/internal/version.txt + git commit -m "chore: release $VERSION + + Automated release created by GitHub Actions + + 🤖 Generated with [Claude Code](https://claude.ai/code) + + Co-Authored-By: Claude " + + # Create and push tag + git tag -a "$VERSION" -m "Release $VERSION" + git push origin master + git push origin "$VERSION" + + # Create GitHub release with notes + gh release create "$VERSION" \ + --title "Release $VERSION" \ + --notes-file release_notes_v2.md \ + --target master + + - name: Log dry-run results for v2 + if: github.event.inputs.dry_run == 'true' + run: | + echo "🧪 DRY RUN - Would have created v2 release:" + echo "Version: ${{ steps.release.outputs.version }}" + echo "Release Notes:" + cat release_notes_v2.md + + release-v3: + name: Create v3-alpha Release + runs-on: ubuntu-latest + needs: [check-permissions, detect-v3-changes] + if: | + needs.check-permissions.outputs.authorized == 'true' && + needs.detect-v3-changes.outputs.has_changes == 'true' + outputs: + version: ${{ steps.release.outputs.version }} + release_notes: ${{ steps.release.outputs.release_notes }} + steps: + - name: Checkout v3-alpha branch + uses: actions/checkout@v4 + with: + ref: v3-alpha + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Run v3 release script and extract notes + id: release + run: | + echo "🚀 Running v3-alpha release script..." + cd v3/tasks/release + + # Run release script and capture output + RELEASE_OUTPUT=$(go run release.go 2>&1) + echo "$RELEASE_OUTPUT" + + # Extract version from output or version file + NEW_VERSION=$(cat ../../internal/version/version.txt) + echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT + + # Extract release notes from delimited output + RELEASE_NOTES=$(echo "$RELEASE_OUTPUT" | sed -n '/=== RELEASE NOTES FOR/,/=== END RELEASE NOTES ===/p' | sed '1d;$d') + + # Save release notes to file for multiline output + echo "$RELEASE_NOTES" > ../../../release_notes_v3.md + + # Set output (escape for GitHub Actions) + { + echo "release_notes<> $GITHUB_OUTPUT + + echo "✅ v3-alpha release script completed - version: $NEW_VERSION" + + - name: Create v3-alpha git tag and release + if: github.event.inputs.dry_run != 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ steps.release.outputs.version }}" + echo "📝 Creating v3-alpha release: $VERSION" + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Commit the changelog changes + git add docs/src/content/docs/changelog.mdx v3/internal/version/version.txt + git commit -m "chore: release $VERSION + + Automated v3-alpha release created by GitHub Actions + + 🤖 Generated with [Claude Code](https://claude.ai/code) + + Co-Authored-By: Claude " + + # Create and push tag + git tag -a "$VERSION" -m "Release $VERSION" + git push origin v3-alpha + git push origin "$VERSION" + + # Create GitHub release with notes + gh release create "$VERSION" \ + --title "Release $VERSION" \ + --notes-file release_notes_v3.md \ + --target v3-alpha \ + --prerelease + + - name: Log dry-run results for v3-alpha + if: github.event.inputs.dry_run == 'true' + run: | + echo "🧪 DRY RUN - Would have created v3-alpha release:" + echo "Version: ${{ steps.release.outputs.version }}" + echo "Release Notes:" + cat release_notes_v3.md + + summary: + name: Release Summary + runs-on: ubuntu-latest + needs: [check-permissions, detect-v2-changes, detect-v3-changes, release-v2, release-v3] + if: always() && needs.check-permissions.outputs.authorized == 'true' + steps: + - name: Create release summary + run: | + echo "# 🚀 Automated Release Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Repository**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY + echo "**Triggered by**: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY + echo "**Dry Run Mode**: ${{ github.event.inputs.dry_run || 'false' }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # v2 Summary + echo "## v2 Release" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.detect-v2-changes.outputs.has_changes }}" == "true" ]; then + if [ "${{ needs.release-v2.result }}" == "success" ]; then + echo "✅ **v2 Release**: Created successfully" >> $GITHUB_STEP_SUMMARY + echo " - Version: ${{ needs.release-v2.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo " - Commits since last: ${{ needs.detect-v2-changes.outputs.commits_since_last }}" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **v2 Release**: Failed" >> $GITHUB_STEP_SUMMARY + fi + else + echo "⏭️ **v2 Release**: Skipped (no changes)" >> $GITHUB_STEP_SUMMARY + echo " - Commits since last: ${{ needs.detect-v2-changes.outputs.commits_since_last }}" >> $GITHUB_STEP_SUMMARY + fi + + # v3 Summary + echo "## v3-alpha Release" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.detect-v3-changes.outputs.has_changes }}" == "true" ]; then + if [ "${{ needs.release-v3.result }}" == "success" ]; then + echo "✅ **v3-alpha Release**: Created successfully" >> $GITHUB_STEP_SUMMARY + echo " - Version: ${{ needs.release-v3.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo " - Commits since last: ${{ needs.detect-v3-changes.outputs.commits_since_last }}" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **v3-alpha Release**: Failed" >> $GITHUB_STEP_SUMMARY + fi + else + echo "⏭️ **v3-alpha Release**: Skipped (no changes)" >> $GITHUB_STEP_SUMMARY + echo " - Commits since last: ${{ needs.detect-v3-changes.outputs.commits_since_last }}" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "🤖 **Automated Release System** | Generated with [Claude Code](https://claude.ai/code)" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/build-and-test-v3.yml b/.github/workflows/build-and-test-v3.yml new file mode 100644 index 000000000..a1b482e11 --- /dev/null +++ b/.github/workflows/build-and-test-v3.yml @@ -0,0 +1,256 @@ +name: Build + Test v3 + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: + - v3-alpha + paths: + - 'v3/**' + pull_request_review: + types: [submitted] + branches: + - v3-alpha + +jobs: + check_approval: + name: Check PR Approval + runs-on: ubuntu-latest + if: github.base_ref == 'v3-alpha' + outputs: + approved: ${{ steps.check.outputs.approved }} + steps: + - name: Check if PR is approved + id: check + run: | + if [[ "${{ github.event.review.state }}" == "approved" || "${{ github.event.pull_request.approved }}" == "true" ]]; then + echo "approved=true" >> $GITHUB_OUTPUT + else + echo "approved=false" >> $GITHUB_OUTPUT + fi + + test_js: + name: Run JS Tests + needs: check_approval + runs-on: ubuntu-latest + if: github.base_ref == 'v3-alpha' + strategy: + matrix: + node-version: [20.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install dependencies + working-directory: v3/internal/runtime/desktop/@wailsio/runtime + run: | + npm ci + npx --yes esbuild@latest --version + + - name: Clean build artifacts + working-directory: v3/internal/runtime/desktop/@wailsio/runtime + run: npm run clean + + - name: Type-check runtime + working-directory: v3 + run: task runtime:check + + - name: Test runtime + working-directory: v3 + run: task runtime:test + + - name: Check that the bundled runtime builds + working-directory: v3 + run: task runtime:build + + - name: Check that the npm package builds + working-directory: v3/internal/runtime/desktop/@wailsio/runtime + run: npm run build + + - name: Store runtime build artifacts + uses: actions/upload-artifact@v4 + with: + name: runtime-build-artifacts + path: | + v3/internal/runtime/desktop/@wailsio/runtime/dist/ + v3/internal/runtime/desktop/@wailsio/runtime/types/ + v3/internal/runtime/desktop/@wailsio/runtime/tsconfig.tsbuildinfo + + test_go: + name: Run Go Tests v3 + needs: [check_approval, test_js] + runs-on: ${{ matrix.os }} + if: github.base_ref == 'v3-alpha' + strategy: + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + go-version: [1.24] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install linux dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + if: matrix.os == 'ubuntu-latest' + with: + packages: libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config xvfb x11-xserver-utils at-spi2-core xdg-desktop-portal-gtk + version: 1.0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + cache: true + cache-dependency-path: "v3/go.sum" + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Retrieve runtime build artifacts + uses: actions/download-artifact@v4 + with: + name: runtime-build-artifacts + path: v3/internal/runtime/desktop/@wailsio/runtime/ + + - name: Build Examples + working-directory: v3 + run: | + echo "Starting example compilation tests..." + task test:examples + echo "Example compilation tests completed successfully" + + - name: Run tests (mac) + if: matrix.os == 'macos-latest' + env: + CGO_LDFLAGS: -framework UniformTypeIdentifiers -mmacosx-version-min=10.13 + working-directory: v3 + run: go test -v ./... + + - name: Run tests (windows) + if: matrix.os == 'windows-latest' + working-directory: v3 + run: go test -v ./... + + - name: Run tests (ubuntu) + if: matrix.os == 'ubuntu-latest' + working-directory: v3 + run: > + xvfb-run --auto-servernum + sh -c ' + dbus-update-activation-environment --systemd --all && + go test -v ./... + ' + + - name: Typecheck binding generator output + working-directory: v3 + run: task generator:test:check + + cleanup: + name: Cleanup build artifacts + if: always() + needs: [test_js, test_go] + runs-on: ubuntu-latest + steps: + - uses: geekyeggo/delete-artifact@v5 + with: + name: runtime-build-artifacts + failOnError: false + + test_templates: + name: Test Templates + needs: test_go + runs-on: ${{ matrix.os }} + if: github.base_ref == 'v3-alpha' + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + template: + - svelte + - svelte-ts + - vue + - vue-ts + - react + - react-ts + - preact + - preact-ts + - lit + - lit-ts + - vanilla + - vanilla-ts + go-version: [1.24] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install linux dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + if: matrix.os == 'ubuntu-latest' + with: + packages: libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config + version: 1.0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + cache: true + cache-dependency-path: "v3/go.sum" + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Wails3 CLI + working-directory: v3 + run: | + task install + wails3 doctor + + - name: Generate template '${{ matrix.template }}' + run: | + mkdir -p ./test-${{ matrix.template }} + cd ./test-${{ matrix.template }} + wails3 init -n ${{ matrix.template }} -t ${{ matrix.template }} + cd ${{ matrix.template }} + wails3 build + + build_results: + if: ${{ always() }} + runs-on: ubuntu-latest + name: v3 Build Results + needs: [test_go, test_js, test_templates] + steps: + - run: | + go_result="${{ needs.test_go.result }}" + js_result="${{ needs.test_js.result }}" + templates_result="${{ needs.test_templates.result }}" + + if [[ $go_result == "success" || $go_result == "skipped" ]] && \ + [[ $js_result == "success" || $js_result == "skipped" ]] && \ + [[ $templates_result == "success" || $templates_result == "skipped" ]]; then + echo "All required jobs succeeded or were skipped" + exit 0 + else + echo "One or more required jobs failed" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/changelog-validation-v3.yml b/.github/workflows/changelog-validation-v3.yml new file mode 100644 index 000000000..8d4b97726 --- /dev/null +++ b/.github/workflows/changelog-validation-v3.yml @@ -0,0 +1,74 @@ +name: Changelog Validation (v3) + +on: + pull_request: + branches: [ v3-alpha ] + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to validate (for manual testing)' + required: true + type: string + +jobs: + validate-changelog: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event.inputs.pr_number + + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || format('refs/pull/{0}/head', github.event.inputs.pr_number) }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.23' + + - name: Get PR information + id: pr_info + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT + echo "base_ref=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT + else + echo "pr_number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT + echo "base_ref=v3-alpha" >> $GITHUB_OUTPUT + fi + + - name: Check if changelog was modified + id: changelog_check + run: | + git fetch origin ${{ steps.pr_info.outputs.base_ref }} + if git diff --name-only origin/${{ steps.pr_info.outputs.base_ref }}..HEAD | grep -q "v3/UNRELEASED_CHANGELOG.md"; then + echo "changelog_modified=true" >> $GITHUB_OUTPUT + echo "✅ UNRELEASED_CHANGELOG.md was modified in this PR" + else + echo "changelog_modified=false" >> $GITHUB_OUTPUT + echo "⚠️ UNRELEASED_CHANGELOG.md was not modified" + fi + + - name: Comment on PR about missing changelog + if: steps.changelog_check.outputs.changelog_modified == 'false' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const author = context.payload.pull_request.user.login; + const message = '## ⚠️ Missing Changelog Update\n\n' + + `Hi @${author}, please update \`v3/UNRELEASED_CHANGELOG.md\` with a description of your changes.\n\n` + + 'This helps us keep track of changes for the next release.'; + + await github.rest.issues.createComment({ + issue_number: ${{ steps.pr_info.outputs.pr_number }}, + owner: context.repo.owner, + repo: context.repo.repo, + body: message + }); + diff --git a/.github/workflows/generate-sponsor-image.yml b/.github/workflows/generate-sponsor-image.yml index b8bb0c4ac..585d7e19f 100644 --- a/.github/workflows/generate-sponsor-image.yml +++ b/.github/workflows/generate-sponsor-image.yml @@ -29,7 +29,12 @@ jobs: with: commit-message: "chore: update sponsors.svg" add-paths: "website/static/img/sponsors.svg" - title: Update Sponsor Image - body: Generated new image + title: "chore: update sponsors.svg" + body: | + Auto-generated by the sponsor image workflow + + [skip ci] [skip actions] branch: update-sponsors + base: master delete-branch: true + draft: false diff --git a/.github/workflows/issue-triage-automation.yml b/.github/workflows/issue-triage-automation.yml new file mode 100644 index 000000000..4a827d527 --- /dev/null +++ b/.github/workflows/issue-triage-automation.yml @@ -0,0 +1,77 @@ +name: Issue Triage Automation + +on: + issues: + types: [opened, reopened, labeled, unlabeled] + +jobs: + triage: + runs-on: ubuntu-latest + permissions: + issues: write + contents: read + steps: + # Request more info for unclear bug reports + - name: Request more info + uses: actions/github-script@v6 + if: | + contains(github.event.issue.labels.*.name, 'bug') && + !contains(github.event.issue.body, 'wails doctor') && + !contains(github.event.issue.body, 'reproduction') + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `👋 Thanks for reporting this issue! To help us investigate, could you please: + + 1. Add the output of \`wails doctor\` if not already included + 2. Provide clear steps to reproduce the issue + 3. If possible, create a minimal reproduction of the issue + + This will help us resolve your issue much faster. Thank you!` + }); + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['awaiting feedback'] + }); + + # Prioritize security issues + - name: Prioritize security issues + uses: actions/github-script@v6 + if: contains(github.event.issue.labels.*.name, 'security') + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['high-priority'] + }); + + # Tag version-specific issues for project boards + - name: Add to v2 project + uses: actions/github-script@v6 + if: | + contains(github.event.issue.labels.*.name, 'v2-only') && + !contains(github.event.issue.labels.*.name, 'v3-alpha') + with: + script: | + // Replace PROJECT_ID with your actual GitHub project ID + // This is a placeholder as the actual implementation would require + // GraphQL API calls to add to a project board + console.log('Would add to v2 project board'); + + # Tag version-specific issues for project boards + - name: Add to v3 project + uses: actions/github-script@v6 + if: contains(github.event.issue.labels.*.name, 'v3-alpha') + with: + script: | + // Replace PROJECT_ID with your actual GitHub project ID + // This is a placeholder as the actual implementation would require + // GraphQL API calls to add to a project board + console.log('Would add to v3 project board'); diff --git a/.github/workflows/nightly-release-v3.yml b/.github/workflows/nightly-release-v3.yml new file mode 100644 index 000000000..b7df38238 --- /dev/null +++ b/.github/workflows/nightly-release-v3.yml @@ -0,0 +1,729 @@ +name: Nightly Release v3-alpha + +on: + schedule: + - cron: '0 2 * * *' # 2 AM UTC daily + workflow_dispatch: + inputs: + force_release: + description: 'Force release even if no changes detected' + required: false + default: false + type: boolean + dry_run: + description: 'Run in dry-run mode (no actual release)' + required: false + default: true + type: boolean + +jobs: + nightly-release: + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: read + actions: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: v3-alpha + fetch-depth: 0 + token: ${{ secrets.WAILS_REPO_TOKEN || github.token }} + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.24' + cache: true + cache-dependency-path: 'v3/go.sum' + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + # Configure git to use the token for authentication + git config --global url."https://x-access-token:${{ secrets.WAILS_REPO_TOKEN || github.token }}@github.com/".insteadOf "https://github.com/" + + - name: Check for existing release tag + id: check_tag + run: | + if git describe --tags --exact-match HEAD 2>/dev/null; then + echo "has_tag=true" >> $GITHUB_OUTPUT + echo "tag=$(git describe --tags --exact-match HEAD)" >> $GITHUB_OUTPUT + else + echo "has_tag=false" >> $GITHUB_OUTPUT + echo "tag=" >> $GITHUB_OUTPUT + fi + + - name: Check for unreleased changelog content + id: changelog_check + run: | + echo "🔍 Checking UNRELEASED_CHANGELOG.md for content..." + + # Run the release script in check mode to see if there's content + cd v3/tasks/release + + # Use the release script itself to check for content + if go run release.go --check-only 2>/dev/null; then + echo "has_unreleased_content=true" >> $GITHUB_OUTPUT + echo "✅ Found unreleased changelog content" + else + echo "has_unreleased_content=false" >> $GITHUB_OUTPUT + echo "ℹ️ No unreleased changelog content found" + fi + + - name: Quick change detection and early exit + id: quick_check + run: | + echo "🔍 Quick check for changes to determine if we should continue..." + + # First check if we have unreleased changelog content + if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then + echo "✅ Found unreleased changelog content, proceeding with release" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "should_continue=true" >> $GITHUB_OUTPUT + echo "reason=Found unreleased changelog content" >> $GITHUB_OUTPUT + exit 0 + fi + + # If no unreleased changelog content, check for git changes as fallback + echo "No unreleased changelog content found, checking for git changes..." + + # Check if current commit has a release tag + if git describe --tags --exact-match HEAD 2>/dev/null; then + CURRENT_TAG=$(git describe --tags --exact-match HEAD) + echo "Current commit has release tag: $CURRENT_TAG" + + # For tagged commits, check if there are changes since the tag + COMMIT_COUNT=$(git rev-list ${CURRENT_TAG}..HEAD --count) + if [ "$COMMIT_COUNT" -eq 0 ]; then + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "should_continue=false" >> $GITHUB_OUTPUT + echo "reason=No changes since existing tag $CURRENT_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "should_continue=true" >> $GITHUB_OUTPUT + fi + else + # No current tag, check against latest release + LATEST_TAG=$(git tag --list "v3.0.0-alpha.*" | sort -V | tail -1) + if [ -z "$LATEST_TAG" ]; then + echo "No previous release found, proceeding with release" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "should_continue=true" >> $GITHUB_OUTPUT + else + COMMIT_COUNT=$(git rev-list ${LATEST_TAG}..HEAD --count) + if [ "$COMMIT_COUNT" -gt 0 ]; then + echo "Found $COMMIT_COUNT commits since $LATEST_TAG" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "should_continue=true" >> $GITHUB_OUTPUT + else + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "should_continue=false" >> $GITHUB_OUTPUT + echo "reason=No changes since latest release $LATEST_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT + fi + fi + fi + + - name: Early exit - No changes detected + if: | + steps.quick_check.outputs.should_continue == 'false' && + github.event.inputs.force_release != 'true' + run: | + echo "🛑 EARLY EXIT: ${{ steps.quick_check.outputs.reason }}" + echo "" + echo "ℹ️ No changes detected since last release and force_release is not enabled." + echo " Workflow will exit early to save resources." + echo "" + echo " To force a release anyway, run this workflow with 'force_release=true'" + echo "" + echo "## 🛑 Early Exit Summary" >> $GITHUB_STEP_SUMMARY + echo "**Reason:** ${{ steps.quick_check.outputs.reason }}" >> $GITHUB_STEP_SUMMARY + echo "**Action:** Workflow exited early to save resources" >> $GITHUB_STEP_SUMMARY + echo "**Force Release:** Set 'force_release=true' to override this behavior" >> $GITHUB_STEP_SUMMARY + exit 0 + + - name: Continue with release process + if: | + steps.quick_check.outputs.should_continue == 'true' || + github.event.inputs.force_release == 'true' + run: | + echo "✅ Proceeding with release process..." + if [ "${{ github.event.inputs.force_release }}" == "true" ]; then + echo "🔨 FORCE RELEASE: Overriding change detection" + fi + + - name: Run release script + id: release + if: | + steps.quick_check.outputs.should_continue == 'true' || + github.event.inputs.force_release == 'true' + run: | + cd v3 + + echo "🚀 Running release task..." + echo "=======================================================" + + # Initialize error tracking + RELEASE_ERRORS="" + RELEASE_SUCCESS=true + + # Store the original version for comparison + ORIGINAL_VERSION=$(cat internal/version/version.txt 2>/dev/null || echo "unknown") + echo "📌 Current version: $ORIGINAL_VERSION" + + # Run the release task and capture output with error handling + if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then + echo "🧪 DRY RUN MODE: Simulating release task execution" + # In dry run, we'll simulate the task without making actual changes + OUTPUT=$(task release 2>&1 || true) + RELEASE_EXIT_CODE=0 # Always succeed in dry run + echo "$OUTPUT" + else + echo "🚀 LIVE MODE: Executing release task" + OUTPUT=$(task release 2>&1) + RELEASE_EXIT_CODE=$? + echo "$OUTPUT" + + if [ $RELEASE_EXIT_CODE -ne 0 ]; then + echo "❌ Release task failed with exit code $RELEASE_EXIT_CODE" + RELEASE_ERRORS="$RELEASE_ERRORS\n- Release task execution failed: $OUTPUT" + RELEASE_SUCCESS=false + else + echo "✅ Release task completed successfully" + fi + fi + + # Verify version file exists and is readable + if [ ! -f "internal/version/version.txt" ]; then + echo "❌ Version file not found: internal/version/version.txt" + RELEASE_ERRORS="$RELEASE_ERRORS\n- Version file not found after release task execution" + RELEASE_SUCCESS=false + RELEASE_VERSION="unknown" + else + RELEASE_VERSION=$(cat internal/version/version.txt 2>/dev/null || echo "unknown") + if [ "$RELEASE_VERSION" == "unknown" ]; then + echo "❌ Failed to read version from file" + RELEASE_ERRORS="$RELEASE_ERRORS\n- Failed to read version from version.txt" + RELEASE_SUCCESS=false + else + echo "✅ Successfully read version: $RELEASE_VERSION" + fi + fi + + # Check if version changed + VERSION_CHANGED="false" + if [ "$ORIGINAL_VERSION" != "$RELEASE_VERSION" ] && [ "$RELEASE_VERSION" != "unknown" ]; then + echo "✅ Version changed from $ORIGINAL_VERSION to $RELEASE_VERSION" + VERSION_CHANGED="true" + else + echo "ℹ️ Version unchanged: $RELEASE_VERSION" + fi + + RELEASE_TAG="${RELEASE_VERSION}" + RELEASE_TITLE="Wails ${RELEASE_VERSION}" + + # Set outputs for next steps + echo "version=$RELEASE_VERSION" >> $GITHUB_OUTPUT + echo "tag=$RELEASE_TAG" >> $GITHUB_OUTPUT + echo "title=$RELEASE_TITLE" >> $GITHUB_OUTPUT + echo "is_prerelease=true" >> $GITHUB_OUTPUT + echo "is_latest=false" >> $GITHUB_OUTPUT + echo "has_changes=${{ steps.changelog_check.outputs.has_unreleased_content }}" >> $GITHUB_OUTPUT + echo "success=$RELEASE_SUCCESS" >> $GITHUB_OUTPUT + echo "version_changed=$VERSION_CHANGED" >> $GITHUB_OUTPUT + + # Generate release notes from UNRELEASED_CHANGELOG.md if it has content + if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ] && [ "$RELEASE_SUCCESS" == "true" ]; then + echo "📝 Generating release notes from UNRELEASED_CHANGELOG.md..." + + # Use the release script to extract changelog content + cd tasks/release + if CHANGELOG_CONTENT=$(go run release.go --extract-changelog 2>&1); then + if [ -n "$CHANGELOG_CONTENT" ] && [ "$CHANGELOG_CONTENT" != "No changelog content found." ]; then + echo "### Changes in this release:" > ../../release-notes.txt + echo "" >> ../../release-notes.txt + echo "$CHANGELOG_CONTENT" >> ../../release-notes.txt + echo "✅ Successfully extracted changelog content" + echo "release_notes_file=release-notes.txt" >> $GITHUB_OUTPUT + else + echo "ℹ️ No changelog content to extract" + echo "No detailed changelog available for this release." > ../../release-notes.txt + echo "release_notes_file=release-notes.txt" >> $GITHUB_OUTPUT + fi + else + echo "⚠️ Failed to extract changelog content: $CHANGELOG_CONTENT" + echo "No detailed changelog available for this release." > ../../release-notes.txt + RELEASE_ERRORS="$RELEASE_ERRORS\n- Failed to extract changelog content for release notes" + echo "release_notes_file=release-notes.txt" >> $GITHUB_OUTPUT + fi + cd ../.. + else + echo "release_notes_file=" >> $GITHUB_OUTPUT + if [ "$RELEASE_SUCCESS" != "true" ]; then + echo "⚠️ Skipping release notes generation due to release task failure" + fi + fi + + # Set error output for later steps + if [ -n "$RELEASE_ERRORS" ]; then + echo "release_errors<> $GITHUB_OUTPUT + echo -e "$RELEASE_ERRORS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_release_errors=true" >> $GITHUB_OUTPUT + else + echo "has_release_errors=false" >> $GITHUB_OUTPUT + fi + + - name: Commit and push changes + id: git_commit + if: | + (steps.quick_check.outputs.should_continue == 'true' || github.event.inputs.force_release == 'true') && + github.event.inputs.dry_run != 'true' && + steps.release.outputs.success == 'true' && + steps.release.outputs.version_changed == 'true' + env: + GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }} + run: | + echo "📝 Committing and pushing changes..." + + # Initialize error tracking + COMMIT_ERRORS="" + COMMIT_SUCCESS=true + + # Add any changes made by the release script with error handling + if git add . 2>&1; then + echo "✅ Successfully staged changes" + else + echo "❌ Failed to stage changes" + COMMIT_ERRORS="$COMMIT_ERRORS\n- Failed to stage changes with git add" + COMMIT_SUCCESS=false + fi + + # Check if there are changes to commit + if [ "$COMMIT_SUCCESS" == "true" ]; then + if ! git diff --cached --quiet; then + echo "📝 Changes detected, creating commit..." + + # Create commit with error handling + if git commit -m "${{ steps.release.outputs.version }}" 2>&1; then + echo "✅ Successfully created commit" + + # Push changes with retry logic + RETRY_COUNT=0 + MAX_RETRIES=3 + PUSH_SUCCESS=false + + while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ "$PUSH_SUCCESS" == "false" ]; do + RETRY_COUNT=$((RETRY_COUNT + 1)) + echo "🔄 Attempting to push changes (attempt $RETRY_COUNT/$MAX_RETRIES)..." + + if git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" v3-alpha 2>&1; then + echo "✅ Successfully pushed changes to v3-alpha branch" + PUSH_SUCCESS=true + else + echo "❌ Failed to push changes (attempt $RETRY_COUNT/$MAX_RETRIES)" + if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then + echo "⏳ Waiting 5 seconds before retry..." + sleep 5 + fi + fi + done + + if [ "$PUSH_SUCCESS" == "false" ]; then + echo "❌ Failed to push changes after $MAX_RETRIES attempts" + COMMIT_ERRORS="$COMMIT_ERRORS\n- Failed to push changes after $MAX_RETRIES attempts" + COMMIT_SUCCESS=false + fi + else + echo "❌ Failed to create commit" + COMMIT_ERRORS="$COMMIT_ERRORS\n- Failed to create git commit" + COMMIT_SUCCESS=false + fi + else + echo "ℹ️ No changes to commit" + fi + fi + + # Set outputs for later steps + echo "success=$COMMIT_SUCCESS" >> $GITHUB_OUTPUT + + if [ -n "$COMMIT_ERRORS" ]; then + echo "commit_errors<> $GITHUB_OUTPUT + echo -e "$COMMIT_ERRORS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_commit_errors=true" >> $GITHUB_OUTPUT + else + echo "has_commit_errors=false" >> $GITHUB_OUTPUT + fi + + - name: Create and push git tag + id: git_tag + if: | + (steps.quick_check.outputs.should_continue == 'true' || github.event.inputs.force_release == 'true') && + steps.check_tag.outputs.has_tag == 'false' && + github.event.inputs.dry_run != 'true' && + steps.release.outputs.success == 'true' && + steps.release.outputs.version_changed == 'true' && + steps.git_commit.outputs.success == 'true' + env: + GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }} + run: | + echo "🏷️ Creating and pushing git tag: ${{ steps.release.outputs.tag }}" + + # Initialize error tracking + GIT_ERRORS="" + GIT_SUCCESS=true + + # Create git tag with error handling + if git tag -a "${{ steps.release.outputs.tag }}" -m "Release ${{ steps.release.outputs.version }}" 2>&1; then + echo "✅ Successfully created git tag: ${{ steps.release.outputs.tag }}" + else + echo "❌ Failed to create git tag" + GIT_ERRORS="$GIT_ERRORS\n- Failed to create git tag: ${{ steps.release.outputs.tag }}" + GIT_SUCCESS=false + fi + + # Push tag with retry logic and error handling + if [ "$GIT_SUCCESS" == "true" ]; then + RETRY_COUNT=0 + MAX_RETRIES=3 + PUSH_SUCCESS=false + + while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ "$PUSH_SUCCESS" == "false" ]; do + RETRY_COUNT=$((RETRY_COUNT + 1)) + echo "🔄 Attempting to push tag (attempt $RETRY_COUNT/$MAX_RETRIES)..." + + if git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" "${{ steps.release.outputs.tag }}" 2>&1; then + echo "✅ Successfully pushed git tag to origin" + PUSH_SUCCESS=true + else + echo "❌ Failed to push git tag (attempt $RETRY_COUNT/$MAX_RETRIES)" + if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then + echo "⏳ Waiting 5 seconds before retry..." + sleep 5 + fi + fi + done + + if [ "$PUSH_SUCCESS" == "false" ]; then + echo "❌ Failed to push git tag after $MAX_RETRIES attempts" + GIT_ERRORS="$GIT_ERRORS\n- Failed to push git tag after $MAX_RETRIES attempts" + GIT_SUCCESS=false + fi + fi + + # Set outputs for later steps + echo "success=$GIT_SUCCESS" >> $GITHUB_OUTPUT + + if [ -n "$GIT_ERRORS" ]; then + echo "git_tag_errors<> $GITHUB_OUTPUT + echo -e "$GIT_ERRORS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_git_errors=true" >> $GITHUB_OUTPUT + else + echo "has_git_errors=false" >> $GITHUB_OUTPUT + fi + + - name: Read release notes + id: read_notes + if: | + (steps.quick_check.outputs.should_continue == 'true' || github.event.inputs.force_release == 'true') && + steps.release.outputs.release_notes_file != '' && + steps.release.outputs.version_changed == 'true' && + steps.git_commit.outputs.success == 'true' + run: | + cd v3 + if [ -f "release-notes.txt" ]; then + # Read the release notes and handle multiline content + RELEASE_NOTES=$(cat release-notes.txt) + echo "release_notes<> $GITHUB_OUTPUT + echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + echo "release_notes=No release notes available" >> $GITHUB_OUTPUT + fi + + - name: Test GitHub Release Creation (DRY RUN) + if: | + (steps.quick_check.outputs.should_continue == 'true' || github.event.inputs.force_release == 'true') && + github.event.inputs.dry_run == 'true' && + steps.release.outputs.version_changed == 'true' + run: | + echo "🧪 DRY RUN: Would create GitHub release with the following parameters:" + echo "=======================================================================" + echo "Tag Name: ${{ steps.release.outputs.tag }}" + echo "Release Name: ${{ steps.release.outputs.title }}" + echo "Is Prerelease: ${{ steps.release.outputs.is_prerelease }}" + echo "Is Latest: ${{ steps.release.outputs.is_latest }}" + echo "Has Changes: ${{ steps.release.outputs.has_changes }}" + echo "" + echo "Release Body Preview:" + echo "## Wails v3 Alpha Release - ${{ steps.release.outputs.version }}" + echo "" + cat << 'RELEASE_NOTES_EOF' + ${{ steps.read_notes.outputs.release_notes }} + RELEASE_NOTES_EOF + echo "" + echo "" + echo "" + echo "---" + echo "" + echo "🤖 This is an automated nightly release generated from the latest changes in the v3-alpha branch." + echo "" + echo "**Installation:**" + echo "\`\`\`bash" + echo "go install github.com/wailsapp/wails/v3/cmd/wails@${{ steps.release.outputs.tag }}" + echo "\`\`\`" + echo "" + echo "**⚠️ Alpha Warning:** This is pre-release software and may contain bugs or incomplete features." + echo "" + echo "✅ DRY RUN: GitHub release creation test completed successfully!" + + - name: Create GitHub Release (LIVE) + id: github_release + if: | + (steps.quick_check.outputs.should_continue == 'true' || github.event.inputs.force_release == 'true') && + github.event.inputs.dry_run != 'true' && + steps.release.outputs.success == 'true' && + steps.release.outputs.version_changed == 'true' && + steps.git_commit.outputs.success == 'true' && + steps.git_tag.outputs.success == 'true' + continue-on-error: true + run: | + echo "🚀 Creating GitHub release using gh CLI..." + + # Create release notes in a temporary file + cat > release_notes.md << 'EOF' + ## Wails v3 Alpha Release - ${{ steps.release.outputs.version }} + + ${{ steps.read_notes.outputs.release_notes }} + + + + --- + + 🤖 This is an automated nightly release generated from the latest changes in the v3-alpha branch. + + **Installation:** + ```bash + go install github.com/wailsapp/wails/v3/cmd/wails@${{ steps.release.outputs.tag }} + ``` + + **⚠️ Alpha Warning:** This is pre-release software and may contain bugs or incomplete features. + EOF + + # Create the release + if gh release create "${{ steps.release.outputs.tag }}" \ + --title "${{ steps.release.outputs.title }}" \ + --notes-file release_notes.md \ + --target v3-alpha \ + --prerelease; then + echo "✅ Successfully created GitHub release" + echo "outcome=success" >> $GITHUB_OUTPUT + else + echo "❌ Failed to create GitHub release" + echo "outcome=failure" >> $GITHUB_OUTPUT + fi + env: + GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }} + + - name: Handle GitHub Release Creation Result + id: release_result + if: | + (steps.quick_check.outputs.should_continue == 'true' || github.event.inputs.force_release == 'true') && + github.event.inputs.dry_run != 'true' && + steps.release.outputs.success == 'true' && + steps.release.outputs.version_changed == 'true' && + steps.git_commit.outputs.success == 'true' && + steps.git_tag.outputs.success == 'true' + run: | + echo "📋 Checking GitHub release creation result..." + + # Initialize error tracking + GITHUB_ERRORS="" + GITHUB_SUCCESS=true + + # Check if GitHub release creation succeeded + if [ "${{ steps.github_release.outcome }}" == "success" ]; then + echo "✅ GitHub release created successfully" + echo "🔗 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ steps.release.outputs.tag }}" + else + echo "❌ GitHub release creation failed" + GITHUB_ERRORS="$GITHUB_ERRORS\n- GitHub release creation failed with outcome: ${{ steps.github_release.outcome }}" + GITHUB_SUCCESS=false + fi + + # Set outputs for summary + echo "success=$GITHUB_SUCCESS" >> $GITHUB_OUTPUT + + if [ -n "$GITHUB_ERRORS" ]; then + echo "github_errors<> $GITHUB_OUTPUT + echo -e "$GITHUB_ERRORS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_github_errors=true" >> $GITHUB_OUTPUT + else + echo "has_github_errors=false" >> $GITHUB_OUTPUT + fi + + - name: Error Summary and Reporting + id: error_summary + if: always() + run: | + echo "📊 Generating comprehensive error summary..." + + # Initialize error tracking + TOTAL_ERRORS=0 + ERROR_SUMMARY="" + OVERALL_SUCCESS=true + + # Check for changelog errors + if [ "${{ steps.changelog_check.outputs.has_errors }}" == "true" ]; then + echo "❌ Changelog processing errors detected" + ERROR_SUMMARY="$ERROR_SUMMARY\n### 📄 Changelog Processing Errors\n${{ steps.changelog_check.outputs.changelog_errors }}\n" + TOTAL_ERRORS=$((TOTAL_ERRORS + 1)) + OVERALL_SUCCESS=false + fi + + # Check for release script errors + if [ "${{ steps.release.outputs.has_release_errors }}" == "true" ]; then + echo "❌ Release script errors detected" + ERROR_SUMMARY="$ERROR_SUMMARY\n### 🚀 Release Script Errors\n${{ steps.release.outputs.release_errors }}\n" + TOTAL_ERRORS=$((TOTAL_ERRORS + 1)) + OVERALL_SUCCESS=false + fi + + # Check for git tag errors + if [ "${{ steps.git_tag.outputs.has_git_errors }}" == "true" ]; then + echo "❌ Git tag errors detected" + ERROR_SUMMARY="$ERROR_SUMMARY\n### 🏷️ Git Tag Errors\n${{ steps.git_tag.outputs.git_tag_errors }}\n" + TOTAL_ERRORS=$((TOTAL_ERRORS + 1)) + OVERALL_SUCCESS=false + fi + + # Check for git commit errors + if [ "${{ steps.git_commit.outputs.has_commit_errors }}" == "true" ]; then + echo "❌ Git commit errors detected" + ERROR_SUMMARY="$ERROR_SUMMARY\n### 📝 Git Commit Errors\n${{ steps.git_commit.outputs.commit_errors }}\n" + TOTAL_ERRORS=$((TOTAL_ERRORS + 1)) + OVERALL_SUCCESS=false + fi + + # Check for GitHub release errors + if [ "${{ steps.release_result.outputs.has_github_errors }}" == "true" ]; then + echo "❌ GitHub release errors detected" + ERROR_SUMMARY="$ERROR_SUMMARY\n### 🐙 GitHub Release Errors\n${{ steps.release_result.outputs.github_errors }}\n" + TOTAL_ERRORS=$((TOTAL_ERRORS + 1)) + OVERALL_SUCCESS=false + fi + + # Set outputs for final summary + echo "total_errors=$TOTAL_ERRORS" >> $GITHUB_OUTPUT + echo "overall_success=$OVERALL_SUCCESS" >> $GITHUB_OUTPUT + + if [ -n "$ERROR_SUMMARY" ]; then + echo "error_summary<> $GITHUB_OUTPUT + echo -e "$ERROR_SUMMARY" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + # Log summary + if [ "$OVERALL_SUCCESS" == "true" ]; then + echo "✅ Workflow completed successfully with no errors" + else + echo "⚠️ Workflow completed with $TOTAL_ERRORS error categories" + fi + + - name: Summary + if: always() + run: | + if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then + echo "## 🧪 DRY RUN Release Test Summary" >> $GITHUB_STEP_SUMMARY + else + echo "## 🚀 Nightly Release Summary" >> $GITHUB_STEP_SUMMARY + fi + echo "================================" >> $GITHUB_STEP_SUMMARY + echo "- **Version:** ${{ steps.release.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Tag:** ${{ steps.release.outputs.tag }}" >> $GITHUB_STEP_SUMMARY + echo "- **Version Changed:** ${{ steps.release.outputs.version_changed }}" >> $GITHUB_STEP_SUMMARY + echo "- **Has existing tag:** ${{ steps.check_tag.outputs.has_tag }}" >> $GITHUB_STEP_SUMMARY + echo "- **Has unreleased changelog content:** ${{ steps.changelog_check.outputs.has_unreleased_content }}" >> $GITHUB_STEP_SUMMARY + echo "- **Has changes:** ${{ steps.release.outputs.has_changes }}" >> $GITHUB_STEP_SUMMARY + echo "- **Is prerelease:** ${{ steps.release.outputs.is_prerelease }}" >> $GITHUB_STEP_SUMMARY + echo "- **Is latest:** ${{ steps.release.outputs.is_latest }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Overall status + if [ "${{ steps.error_summary.outputs.overall_success }}" == "true" ]; then + if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then + echo "- **Mode:** 🧪 DRY RUN (no actual release created)" >> $GITHUB_STEP_SUMMARY + echo "- **Status:** ✅ Test completed successfully" >> $GITHUB_STEP_SUMMARY + else + echo "- **Mode:** 🚀 Live release" >> $GITHUB_STEP_SUMMARY + echo "- **Status:** ✅ Release created successfully" >> $GITHUB_STEP_SUMMARY + fi + else + echo "- **Mode:** ${{ github.event.inputs.dry_run == 'true' && '🧪 DRY RUN' || '🚀 Live release' }}" >> $GITHUB_STEP_SUMMARY + echo "- **Status:** ⚠️ Completed with ${{ steps.error_summary.outputs.total_errors }} error(s)" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Release Processing" >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.release.outputs.version_changed }}" == "true" ]; then + echo "✅ **Version was incremented** and release created" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ **Version was not changed** - no release created" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Changelog Processing" >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then + echo "✅ **UNRELEASED_CHANGELOG.md** had content and was processed" >> $GITHUB_STEP_SUMMARY + echo "- Content moved to main changelog" >> $GITHUB_STEP_SUMMARY + echo "- UNRELEASED_CHANGELOG.md reset with template" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ **UNRELEASED_CHANGELOG.md** had no content to process" >> $GITHUB_STEP_SUMMARY + fi + + # Error reporting section + if [ "${{ steps.error_summary.outputs.total_errors }}" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "## ⚠️ Error Report" >> $GITHUB_STEP_SUMMARY + echo "**Total Error Categories:** ${{ steps.error_summary.outputs.total_errors }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "${{ steps.error_summary.outputs.error_summary }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🔧 Troubleshooting Tips" >> $GITHUB_STEP_SUMMARY + echo "- Check the individual step logs above for detailed error messages" >> $GITHUB_STEP_SUMMARY + echo "- Verify GitHub token permissions (contents: write, pull-requests: read)" >> $GITHUB_STEP_SUMMARY + echo "- Ensure UNRELEASED_CHANGELOG.md follows the expected format" >> $GITHUB_STEP_SUMMARY + echo "- Check for network connectivity issues if git/GitHub operations failed" >> $GITHUB_STEP_SUMMARY + echo "- Re-run the workflow with 'force_release=true' if needed" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Release Notes Preview" >> $GITHUB_STEP_SUMMARY + if [ -n "${{ steps.read_notes.outputs.release_notes }}" ]; then + echo "${{ steps.read_notes.outputs.release_notes }}" >> $GITHUB_STEP_SUMMARY + else + echo "No specific release notes generated" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "*Generated by automated nightly release workflow with enhanced error handling and changelog integration*" >> $GITHUB_STEP_SUMMARY + + # Set final workflow status + if [ "${{ steps.error_summary.outputs.overall_success }}" != "true" ]; then + echo "⚠️ Workflow completed with errors. Check the summary above for details." + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr-master.yml similarity index 66% rename from .github/workflows/pr.yml rename to .github/workflows/pr-master.yml index c70050276..1f9fe5622 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr-master.yml @@ -1,20 +1,23 @@ -name: PR Checks +name: PR Checks (master) on: pull_request: + branches: + - master pull_request_review: types: [submitted] - + branches: + - master jobs: check_docs: name: Check Docs - if: ${{github.repository == 'wailsapp/wails' && contains(github.head_ref,'feature/')}} + if: ${{github.repository == 'wailsapp/wails' && github.base_ref == 'master'}} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Verify Changed files - uses: tj-actions/verify-changed-files@v17 + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 id: verify-changed-files with: files: | @@ -26,47 +29,28 @@ jobs: run: | echo "::warning::Feature branch does not contain any changes to the website." -# lint_go: -# name: Run Go Linters -# runs-on: ubuntu-latest -# steps: -# - name: Checkout code -# uses: actions/checkout@v4 -# -# - name: Setup Go -# uses: actions/setup-go@v4 -# with: -# go-version: "1.21" -# -# - name: Update go modules -# working-directory: ./v2 -# run: go mod tidy -# -# - name: Run Linter -# uses: golangci/golangci-lint-action@v3 -# with: -# version: v1.54 -# working-directory: ./v2 -# args: --timeout=10m0s --config ./.golangci.yml - test_go: name: Run Go Tests runs-on: ${{ matrix.os }} - if: github.event.review.state == 'approved' + if: > + github.event.review.state == 'approved' && + github.repository == 'wailsapp/wails' && + github.base_ref == 'master' && + github.event.pull_request.head.ref != 'update-sponsors' strategy: matrix: os: [ubuntu-22.04, windows-latest, macos-latest, ubuntu-24.04] - go-version: ['1.21'] + go-version: ['1.23'] steps: - name: Checkout code uses: actions/checkout@v3 - - name: Install linux dependencies ( 22.04 ) + - name: Install linux dependencies (22.04) if: matrix.os == 'ubuntu-22.04' run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config - - name: Install linux dependencies ( 24.04 ) + - name: Install linux dependencies (24.04) if: matrix.os == 'ubuntu-24.04' run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml new file mode 100644 index 000000000..1107ed247 --- /dev/null +++ b/.github/workflows/publish-npm.yml @@ -0,0 +1,101 @@ +on: + push: + branches: ['atterpac-npm-publish'] + workflow_dispatch: + +concurrency: + group: publish-npm-v3 + cancel-in-progress: true + +jobs: + detect: + name: Detect committed changes + if: github.event_name != 'workflow_dispatch' + outputs: + changed: ${{ steps.package-json-changes.outputs.any_modified == 'true' || steps.source-changes.outputs.any_modified == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + persist-credentials: 'true' + + - name: Detect committed package.json changes + id: package-json-changes + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 + with: + files: | + v3/internal/runtime/desktop/@wailsio/runtime/package.json + + - name: Detect committed source changes + if: >- + steps.package-json-changes.outputs.any_modified != 'true' + id: source-changes + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 + with: + files: | + v3/internal/runtime/Taskfile.yaml + v3/internal/runtime/desktop/@wailsio/compiled/main.js + v3/internal/runtime/desktop/@wailsio/runtime/tsconfig.json + v3/internal/runtime/desktop/@wailsio/runtime/src/** + v3/pkg/events/events.txt + v3/tasks/events/** + + rebuild_and_publish: + name: Rebuild and publish + needs: [detect] + if: >- + !failure() && !cancelled() + && (github.event_name == 'workflow_dispatch' || needs.detect.outputs.changed == 'true') + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: 'v3-alpha' + ssh-key: ${{ secrets.DEPLOY_KEY }} + + - name: Configure git + run: | + git config --local user.email "github-actions@github.com" + git config --local user.name "GitHub Actions" + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + working-directory: v3/internal/runtime/desktop/@wailsio/runtime + run: | + npm ci + npx --yes esbuild@latest --version + + - name: Clean build artifacts + working-directory: v3/internal/runtime/desktop/@wailsio/runtime + run: npm run clean + + - name: Build bundled runtime + working-directory: v3 + run: task runtime:build + + - name: Test+Build npm package + working-directory: v3/internal/runtime/desktop/@wailsio/runtime + run: | + npm test + npm run build + + - name: Publish npm package + uses: JS-DevTools/npm-publish@v3 + with: + package: v3/internal/runtime/desktop/@wailsio/runtime + access: public + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 453e4cb85..a59818660 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -5,6 +5,7 @@ on: branches: - main - master + - v3-alpha paths: - .github/workflows/semgrep.yml schedule: @@ -14,7 +15,7 @@ name: Semgrep jobs: semgrep: name: semgrep/ci - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 env: SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} container: diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml new file mode 100644 index 000000000..c4ffd25fe --- /dev/null +++ b/.github/workflows/stale-issues.yml @@ -0,0 +1,57 @@ +name: Mark and Close Stale Issues + +on: + schedule: + - cron: '0 1 * * *' # Run at 1 AM UTC every day + workflow_dispatch: # Allow manual triggering + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v9 + with: + # General settings + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 45 + days-before-close: 10 + stale-issue-label: 'stale' + operations-per-run: 250 # Increased from 50 to 250 + + # Issue specific settings + stale-issue-message: | + This issue has been automatically marked as stale because it has not had recent activity. + It will be closed if no further activity occurs within the next 10 days. + + If this issue is still relevant, please add a comment to keep it open. + Thank you for your contributions. + + close-issue-message: | + This issue has been automatically closed due to lack of activity. + Please feel free to reopen it if it's still relevant. + + # PR specific settings - We will not mark PRs as stale + days-before-pr-stale: -1 # Disable PR staling + days-before-pr-close: -1 # Disable PR closing + + # Exemptions + exempt-issue-labels: 'pinned,security,onhold,inprogress,Selected For Development,bug,enhancement,v3-alpha,high-priority' + exempt-all-issue-milestones: true + exempt-all-issue-assignees: true + + # Protection for existing issues + exempt-issue-created-before: '2024-01-01T00:00:00Z' + start-date: '2025-06-01T00:00:00Z' # Don't start checking until June 1, 2025 + + # Only process issues, not PRs + only-labels: '' + any-of-labels: '' + remove-stale-when-updated: true + + # Debug options + debug-only: false # Set to true to test without actually marking issues + ascending: true # Process older issues first diff --git a/.github/workflows/test-nightly-releases.yml b/.github/workflows/test-nightly-releases.yml new file mode 100644 index 000000000..63df09935 --- /dev/null +++ b/.github/workflows/test-nightly-releases.yml @@ -0,0 +1,216 @@ +name: Test Nightly Releases (Dry Run) + +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Run in dry-run mode (no actual releases)' + required: false + default: true + type: boolean + test_branch: + description: 'Branch to test against' + required: false + default: 'master' + type: string + +env: + GO_VERSION: '1.24' + +jobs: + test-permissions: + name: Test Release Permissions + runs-on: ubuntu-latest + outputs: + authorized: ${{ steps.check.outputs.authorized }} + steps: + - name: Check if user is authorized + id: check + run: | + # Test authorization logic + AUTHORIZED_USERS="leaanthony" + + if [[ "$AUTHORIZED_USERS" == *"${{ github.actor }}"* ]]; then + echo "✅ User ${{ github.actor }} is authorized" + echo "authorized=true" >> $GITHUB_OUTPUT + else + echo "❌ User ${{ github.actor }} is not authorized" + echo "authorized=false" >> $GITHUB_OUTPUT + fi + + test-changelog-extraction: + name: Test Changelog Extraction + runs-on: ubuntu-latest + needs: test-permissions + if: needs.test-permissions.outputs.authorized == 'true' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.test_branch }} + fetch-depth: 0 + + - name: Test v2 changelog extraction + run: | + echo "🧪 Testing v2 changelog extraction..." + CHANGELOG_FILE="website/src/pages/changelog.mdx" + + if [ ! -f "$CHANGELOG_FILE" ]; then + echo "❌ v2 changelog file not found" + exit 1 + fi + + # Extract unreleased section + awk ' + /^## \[Unreleased\]/ { found=1; next } + found && /^## / { exit } + found && !/^$/ { print } + ' $CHANGELOG_FILE > v2_release_notes.md + + echo "📝 v2 changelog content (first 10 lines):" + head -10 v2_release_notes.md || echo "No content found" + echo "Total lines: $(wc -l < v2_release_notes.md)" + + - name: Test v3 changelog extraction (if accessible) + run: | + echo "🧪 Testing v3 changelog extraction..." + + if git show v3-alpha:docs/src/content/docs/changelog.mdx > /dev/null 2>&1; then + echo "✅ v3 changelog accessible" + + git show v3-alpha:docs/src/content/docs/changelog.mdx | awk ' + /^## \[Unreleased\]/ { found=1; next } + found && /^## / { exit } + found && !/^$/ { print } + ' > v3_release_notes.md + + echo "📝 v3 changelog content (first 10 lines):" + head -10 v3_release_notes.md || echo "No content found" + echo "Total lines: $(wc -l < v3_release_notes.md)" + else + echo "⚠️ v3 changelog not accessible from current context" + fi + + test-version-detection: + name: Test Version Detection + runs-on: ubuntu-latest + needs: test-permissions + if: needs.test-permissions.outputs.authorized == 'true' + outputs: + v2_current_version: ${{ steps.versions.outputs.v2_current }} + v2_next_patch: ${{ steps.versions.outputs.v2_next_patch }} + v2_next_minor: ${{ steps.versions.outputs.v2_next_minor }} + v2_next_major: ${{ steps.versions.outputs.v2_next_major }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Test version detection logic + id: versions + run: | + echo "🧪 Testing version detection..." + + # Test v2 version parsing + if [ -f "v2/cmd/wails/internal/version.txt" ]; then + CURRENT_V2=$(cat v2/cmd/wails/internal/version.txt | sed 's/^v//') + echo "Current v2 version: v$CURRENT_V2" + echo "v2_current=v$CURRENT_V2" >> $GITHUB_OUTPUT + + # Parse and increment + IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_V2" + MAJOR=${VERSION_PARTS[0]} + MINOR=${VERSION_PARTS[1]} + PATCH=${VERSION_PARTS[2]} + + PATCH_VERSION="v$MAJOR.$MINOR.$((PATCH + 1))" + MINOR_VERSION="v$MAJOR.$((MINOR + 1)).0" + MAJOR_VERSION="v$((MAJOR + 1)).0.0" + + echo "v2_next_patch=$PATCH_VERSION" >> $GITHUB_OUTPUT + echo "v2_next_minor=$MINOR_VERSION" >> $GITHUB_OUTPUT + echo "v2_next_major=$MAJOR_VERSION" >> $GITHUB_OUTPUT + + echo "✅ Patch: v$CURRENT_V2 → $PATCH_VERSION" + echo "✅ Minor: v$CURRENT_V2 → $MINOR_VERSION" + echo "✅ Major: v$CURRENT_V2 → $MAJOR_VERSION" + else + echo "❌ v2 version file not found" + fi + + test-commit-analysis: + name: Test Commit Analysis + runs-on: ubuntu-latest + needs: test-permissions + if: needs.test-permissions.outputs.authorized == 'true' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Test commit analysis + run: | + echo "🧪 Testing commit analysis..." + + # Get recent commits for testing + echo "Recent commits:" + git log --oneline -10 + + # Test conventional commit detection + RECENT_COMMITS=$(git log --oneline --since="7 days ago") + echo "Commits from last 7 days:" + echo "$RECENT_COMMITS" + + # Analyze for release type + RELEASE_TYPE="patch" + if echo "$RECENT_COMMITS" | grep -q "feat!\|fix!\|BREAKING CHANGE:"; then + RELEASE_TYPE="major" + elif echo "$RECENT_COMMITS" | grep -q "feat\|BREAKING CHANGE"; then + RELEASE_TYPE="minor" + fi + + echo "✅ Detected release type: $RELEASE_TYPE" + + test-summary: + name: Test Summary + runs-on: ubuntu-latest + needs: [test-permissions, test-changelog-extraction, test-version-detection, test-commit-analysis] + if: always() + steps: + - name: Print test results + run: | + echo "# 🧪 Nightly Release Workflow Test Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ needs.test-permissions.result }}" == "success" ]; then + echo "✅ **Permissions Test**: Passed" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Permissions Test**: Failed" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-changelog-extraction.result }}" == "success" ]; then + echo "✅ **Changelog Extraction**: Passed" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Changelog Extraction**: Failed" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-version-detection.result }}" == "success" ]; then + echo "✅ **Version Detection**: Passed" >> $GITHUB_STEP_SUMMARY + echo " - Current v2: ${{ needs.test-version-detection.outputs.v2_current_version }}" >> $GITHUB_STEP_SUMMARY + echo " - Next patch: ${{ needs.test-version-detection.outputs.v2_next_patch }}" >> $GITHUB_STEP_SUMMARY + echo " - Next minor: ${{ needs.test-version-detection.outputs.v2_next_minor }}" >> $GITHUB_STEP_SUMMARY + echo " - Next major: ${{ needs.test-version-detection.outputs.v2_next_major }}" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Version Detection**: Failed" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-commit-analysis.result }}" == "success" ]; then + echo "✅ **Commit Analysis**: Passed" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Commit Analysis**: Failed" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This was a dry-run test. No actual releases were created." >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/test-simple.yml b/.github/workflows/test-simple.yml new file mode 100644 index 000000000..8c4c88295 --- /dev/null +++ b/.github/workflows/test-simple.yml @@ -0,0 +1,11 @@ +name: Test Simple + +on: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Test + run: echo "Hello World" \ No newline at end of file diff --git a/.github/workflows/unreleased-changelog-trigger.yml b/.github/workflows/unreleased-changelog-trigger.yml new file mode 100644 index 000000000..381023052 --- /dev/null +++ b/.github/workflows/unreleased-changelog-trigger.yml @@ -0,0 +1,128 @@ +name: Auto Release on Changelog Update + +on: + push: + branches: + - v3-alpha + paths: + - 'v3/UNRELEASED_CHANGELOG.md' + workflow_dispatch: + inputs: + dry_run: + description: 'Run in dry-run mode (no actual release)' + required: false + default: false + type: boolean + +jobs: + check-permissions: + name: Check Release Permissions + runs-on: ubuntu-latest + outputs: + authorized: ${{ steps.check.outputs.authorized }} + steps: + - name: Check if user is authorized for releases + id: check + run: | + # Only allow specific users to trigger releases + AUTHORIZED_USERS="leaanthony" + + if [[ "$AUTHORIZED_USERS" == *"${{ github.actor }}"* ]]; then + echo "✅ User ${{ github.actor }} is authorized for releases" + echo "authorized=true" >> $GITHUB_OUTPUT + else + echo "❌ User ${{ github.actor }} is not authorized for releases" + echo "authorized=false" >> $GITHUB_OUTPUT + fi + + trigger-release: + name: Trigger v3-alpha Release + permissions: + contents: read + runs-on: ubuntu-latest + needs: check-permissions + if: needs.check-permissions.outputs.authorized == 'true' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: v3-alpha + fetch-depth: 0 + token: ${{ secrets.WAILS_REPO_TOKEN || github.token }} + + - name: Check for unreleased changelog content + id: changelog_check + run: | + echo "🔍 Checking UNRELEASED_CHANGELOG.md for content..." + + cd v3 + # Check if UNRELEASED_CHANGELOG.md has actual content beyond the template + if [ -f "UNRELEASED_CHANGELOG.md" ]; then + # Use a simple check for actual content (bullet points starting with -) + CONTENT_LINES=$(grep -E "^\s*-\s+[^[:space:]]" UNRELEASED_CHANGELOG.md | wc -l) + if [ "$CONTENT_LINES" -gt 0 ]; then + echo "✅ Found $CONTENT_LINES content lines in UNRELEASED_CHANGELOG.md" + echo "has_content=true" >> $GITHUB_OUTPUT + else + echo "ℹ️ No actual content found in UNRELEASED_CHANGELOG.md" + echo "has_content=false" >> $GITHUB_OUTPUT + fi + else + echo "❌ UNRELEASED_CHANGELOG.md not found" + echo "has_content=false" >> $GITHUB_OUTPUT + fi + + - name: Trigger nightly release workflow + if: steps.changelog_check.outputs.has_content == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.WAILS_REPO_TOKEN || github.token }} + script: | + const response = await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'nightly-release-v3.yml', + ref: 'v3-alpha', + inputs: { + force_release: 'true', + dry_run: '${{ github.event.inputs.dry_run || "false" }}' + } + }); + + console.log('🚀 Successfully triggered nightly release workflow'); + console.log(`Workflow dispatch response status: ${response.status}`); + + // Create a summary + core.summary + .addHeading('🚀 Auto Release Triggered') + .addRaw('The v3-alpha release workflow has been automatically triggered due to changes in UNRELEASED_CHANGELOG.md') + .addTable([ + [{data: 'Trigger', header: true}, {data: 'Value', header: true}], + ['Repository', context.repo.repo], + ['Branch', 'v3-alpha'], + ['Actor', context.actor], + ['Dry Run', '${{ github.event.inputs.dry_run || "false" }}'], + ['Force Release', 'true'] + ]) + .addRaw('\n---\n*This release was automatically triggered by the unreleased-changelog-trigger workflow*') + .write(); + + - name: No content found + if: steps.changelog_check.outputs.has_content == 'false' + run: | + echo "ℹ️ No content found in UNRELEASED_CHANGELOG.md, skipping release trigger" + echo "## ℹ️ No Release Triggered" >> $GITHUB_STEP_SUMMARY + echo "**Reason:** UNRELEASED_CHANGELOG.md does not contain actual changelog content" >> $GITHUB_STEP_SUMMARY + echo "**Action:** No release workflow was triggered" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "To trigger a release, add actual changelog entries to the UNRELEASED_CHANGELOG.md file." >> $GITHUB_STEP_SUMMARY + + - name: Unauthorized user + if: needs.check-permissions.outputs.authorized == 'false' + run: | + echo "❌ User ${{ github.actor }} is not authorized to trigger releases" + echo "## ❌ Unauthorized Release Attempt" >> $GITHUB_STEP_SUMMARY + echo "**User:** ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY + echo "**Action:** Release trigger was blocked due to insufficient permissions" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Only authorized users can trigger automatic releases via changelog updates." >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/upload-source-documents.yml b/.github/workflows/upload-source-documents.yml index df15246fc..69d6c3e48 100644 --- a/.github/workflows/upload-source-documents.yml +++ b/.github/workflows/upload-source-documents.yml @@ -15,7 +15,7 @@ jobs: - name: Verify Changed files id: changed-files - uses: tj-actions/changed-files@v41 + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 with: files: | website/**/*.mdx diff --git a/.github/workflows/v3-docs.yml b/.github/workflows/v3-docs.yml new file mode 100644 index 000000000..7552d43ab --- /dev/null +++ b/.github/workflows/v3-docs.yml @@ -0,0 +1,42 @@ +name: Deploy to GitHub Pages + +on: + # Trigger the workflow every time you push to the `main` branch + # Using a different branch name? Replace `main` with your branch’s name + push: + branches: [v3-alpha] + # Allows you to run this workflow manually from the Actions tab on GitHub. + workflow_dispatch: + +# Allow this job to clone the repo and create a page deployment +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + runs-on: ubuntu-latest + if: github.event.repository.fork == false + steps: + - name: Checkout your repository using git + uses: actions/checkout@v4 + - name: Install, build, and upload your site output + uses: withastro/action@v2 + with: + path: docs + node-version: 20 # The specific version of Node that should be used to build your site. Defaults to 18. (optional) + # package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional) + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 +env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index e7888b44a..10709495a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,5 +37,55 @@ v2/cmd/wails/internal/commands/initialise/templates/testtemplates/ /websitev3/site/ /v3/examples/plugins/bin/testapp +# V3 Example binaries - ignore executables that match directory names +/v3/examples/badge-custom/badge-custom +/v3/examples/badge/badge +/v3/examples/binding/binding +/v3/examples/cancel-async/cancel-async +/v3/examples/cancel-chaining/cancel-chaining +/v3/examples/clipboard/clipboard +/v3/examples/contextmenus/contextmenus +/v3/examples/dev/dev +/v3/examples/dialogs-basic/dialogs-basic +/v3/examples/dialogs/dialogs +/v3/examples/drag-n-drop/drag-n-drop +/v3/examples/environment/environment +/v3/examples/events-bug/events-bug +/v3/examples/events/events +/v3/examples/file-association/file-association +/v3/examples/frameless/frameless +/v3/examples/gin-example/gin-example +/v3/examples/gin-routing/gin-routing +/v3/examples/gin-service/gin-service +/v3/examples/hide-window/hide-window +/v3/examples/html-dnd-api/html-dnd-api +/v3/examples/ignore-mouse/ignore-mouse +/v3/examples/keybindings/keybindings +/v3/examples/menu/menu +/v3/examples/notifications/notifications +/v3/examples/panic-handling/panic-handling +/v3/examples/plain/plain +/v3/examples/raw-message/raw-message +/v3/examples/screen/screen +/v3/examples/services/services +/v3/examples/show-macos-toolbar/show-macos-toolbar +/v3/examples/single-instance/single-instance +/v3/examples/systray-basic/systray-basic +/v3/examples/systray-custom/systray-custom +/v3/examples/systray-menu/systray-menu +/v3/examples/video/video +/v3/examples/window-api/window-api +/v3/examples/window-call/window-call +/v3/examples/window-menu/window-menu +/v3/examples/window/window +/v3/examples/wml/wml + +# Common binary names in examples +/v3/examples/*/main +/v3/examples/*/app +/v3/examples/*/changeme +/v3/examples/*/testbuild-* + # Temporary called mkdocs, should be renamed to more standard -website or similar -/mkdocs-website/site +/docs/site +.aider* diff --git a/.prettierrc.yml b/.prettierrc.yml index 685d8b6e7..1e2d2dada 100644 --- a/.prettierrc.yml +++ b/.prettierrc.yml @@ -1,6 +1,7 @@ overrides: - files: - "**/*.md" + - "**/*.mdx" options: printWidth: 80 proseWrap: always diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..23caa94af --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +v3alpha.wails.io \ No newline at end of file diff --git a/Taskfile.yaml b/Taskfile.yaml index 7cc165825..051da3e42 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -16,6 +16,11 @@ includes: dir: v3 optional: true + docs: + taskfile: docs + dir: docs + optional: true + tasks: contributors:check: cmds: diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..6240da8b1 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/docs/.npmrc b/docs/.npmrc new file mode 100644 index 000000000..cffe8cdef --- /dev/null +++ b/docs/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/docs/.vscode/extensions.json b/docs/.vscode/extensions.json new file mode 100644 index 000000000..22a15055d --- /dev/null +++ b/docs/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/docs/.vscode/launch.json b/docs/.vscode/launch.json new file mode 100644 index 000000000..d64220976 --- /dev/null +++ b/docs/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 000000000..23caa94af --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +v3alpha.wails.io \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..cf70888f2 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,60 @@ +# Starlight Starter Kit: Basics + +[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) + +```sh +npm create astro@latest -- --template starlight +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) +[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro + Starlight project, you'll see the following folders and +files: + +```sh +. +├── public/ +├── src/ +│ ├── assets/ +│ ├── content/ +│ │ ├── docs/ +│ │ └── config.ts +│ └── env.d.ts +├── astro.config.mjs +├── package.json +└── tsconfig.json +``` + +Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. +Each file is exposed as a route based on its file name. + +Images can be added to `src/assets/` and embedded in Markdown with a relative +link. + +Static assets, like favicons, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Check out [Starlight’s docs](https://starlight.astro.build/), read +[the Astro documentation](https://docs.astro.build), or jump into the +[Astro Discord server](https://astro.build/chat). diff --git a/docs/Taskfile.yml b/docs/Taskfile.yml new file mode 100644 index 000000000..76515fcd8 --- /dev/null +++ b/docs/Taskfile.yml @@ -0,0 +1,30 @@ +# https://taskfile.dev + +version: '3' + +tasks: + + setup: + summary: Setup the project + preconditions: + - sh: npm --version + msg: "Looks like npm isn't installed." + cmds: + - npm install + + dev: + summary: Run the dev server + preconditions: + - sh: npm --version + msg: "Looks like npm isn't installed." + cmds: + - npm run dev + + build: + summary: Build the docs + preconditions: + - sh: npm --version + msg: "Looks like npm isn't installed." + cmds: + - npm run build + diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 000000000..00285d9be --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,145 @@ +// @ts-check +import { defineConfig } from "astro/config"; +import starlight from "@astrojs/starlight"; +import sitemap from "@astrojs/sitemap"; +import starlightLinksValidator from "starlight-links-validator"; +import starlightImageZoom from "starlight-image-zoom"; +import starlightBlog from "starlight-blog"; +import { authors } from "./src/content/authors"; + +// https://astro.build/config +export default defineConfig({ + // TODO: update this + site: "https://wails.io", + trailingSlash: "ignore", + compressHTML: true, + output: "static", + build: { format: "directory" }, + devToolbar: { enabled: true }, + integrations: [ + sitemap(), + starlight({ + title: "", + // If a title is added, also update the delimiter. + titleDelimiter: "", + logo: { + dark: "./src/assets/wails-logo-horizontal-dark.svg", + light: "./src/assets/wails-logo-horizontal-light.svg", + }, + favicon: "./public/favicon.svg", + description: "Build desktop applications using Go & Web Technologies.", + pagefind: true, + customCss: ["./src/stylesheets/extra.css"], + lastUpdated: true, // Note, this needs git clone with fetch depth 0 to work + pagination: true, + editLink: { + // TODO: update this + baseUrl: "https://github.com/wailsapp/wails/edit/v3-alpha/docs", + }, + social: { + github: "https://github.com/wailsapp/wails", + discord: "https://discord.gg/JDdSxwjhGf", + "x.com": "https://x.com/wailsapp", + }, + defaultLocale: "root", + locales: { + root: { label: "English", lang: "en", dir: "ltr" }, + // Example of how a new language is added. + // After this, you create a directory named after the language inside content/docs/ + // with the same structure as the root language + // eg content/docs/gr/changelog.md or content/docs/gr/api/application.mdx + // gr: { label: "Greek", lang: "el", dir: "ltr" }, + }, + plugins: [ + // https://starlight-links-validator.vercel.app/configuration/ + // starlightLinksValidator({ + // exclude: [ + // // TODO: Fix these links in the blog/wails-v2-released file + // // "/docs/reference/options#theme", + // // "/docs/reference/options#customtheme", + // // "/docs/guides/application-development#application-menu", + // // "/docs/reference/runtime/dialog", + // // "/docs/reference/options#windowistranslucent", + // // "/docs/reference/options#windowistranslucent-1", + // // "/docs/guides/windows-installer", + // // "/docs/reference/runtime/intro", + // // "/docs/guides/obfuscated", + // // "/docs/howdoesitwork#calling-bound-go-methods", + // ], + // }), + // https://starlight-image-zoom.vercel.app/configuration/ + starlightImageZoom(), + // https://starlight-blog-docs.vercel.app/configuration + starlightBlog({ + title: "Wails Blog", + authors: authors, + }), + ], + sidebar: [ + { label: "Home", link: "/" }, + { + label: "Getting Started", + autogenerate: { directory: "getting-started", collapsed: false }, + }, + { + label: "Tutorials", + collapsed: true, + autogenerate: { directory: "tutorials", collapsed: true }, + }, + { + label: "What's New", + link: "/whats-new", + badge: { text: "New", variant: "tip" }, + }, + { label: "v3 Alpha Feedback", link: "/feedback" }, + { + label: "Learn", + collapsed: true, + autogenerate: { directory: "learn", collapsed: true }, + }, + { + label: "Guides", + collapsed: true, + autogenerate: { directory: "guides", collapsed: true }, + }, + // { + // label: "API", + // collapsed: true, + // autogenerate: { directory: "api", collapsed: true }, + // }, + { + label: "Community", + collapsed: true, + items: [ + { label: "Links", link: "/community/links" }, + { label: "Templates", link: "/community/templates" }, + { + label: "Showcase", + autogenerate: { + directory: "community/showcase", + collapsed: true, + }, + }, + ], + }, + // { + // label: "Development", + // collapsed: true, + // autogenerate: { directory: "development", collapsed: true }, + // }, + { label: "Status", link: "/status" }, + { label: "Changelog", link: "/changelog" }, + { + label: "Sponsor", + link: "https://github.com/sponsors/leaanthony", + badge: { text: "❤️" }, + }, + { + label: "Credits", + link: "/credits", + badge: { text: "👑" }, + }, + ], + }), + ], +}); diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 000000000..6d5c5f596 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,9980 @@ +{ + "name": "wails-docs", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wails-docs", + "version": "0.0.1", + "dependencies": { + "@astrojs/check": "0.9.4", + "@astrojs/react": "4.1.0", + "@astrojs/starlight": "0.29.2", + "@types/react": "19.0.1", + "@types/react-dom": "19.0.2", + "astro": "4.16.17", + "framer-motion": "11.14.4", + "mermaid": "^10.9.3", + "motion": "11.14.4", + "react": "19.0.0", + "react-dom": "19.0.0", + "sharp": "0.33.5", + "starlight-blog": "0.15.0", + "starlight-image-zoom": "0.9.0", + "starlight-links-validator": "0.13.4", + "starlight-showcases": "0.2.0", + "typescript": "5.7.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@astro-community/astro-embed-twitter": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@astro-community/astro-embed-twitter/-/astro-embed-twitter-0.5.8.tgz", + "integrity": "sha512-O2ptQPw+DfipukK8czjJcTcyVgDsrs3OmrHbc3YmWRglaUTOpSTImzPo076POyNBSWjLaRKloul81DFiAMNjTA==", + "license": "MIT", + "dependencies": { + "@astro-community/astro-embed-utils": "^0.1.0" + }, + "peerDependencies": { + "astro": "^2.0.0 || ^3.0.0-beta || ^4.0.0-beta || ^5.0.0-beta" + } + }, + "node_modules/@astro-community/astro-embed-utils": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@astro-community/astro-embed-utils/-/astro-embed-utils-0.1.3.tgz", + "integrity": "sha512-eiMO+vfCdE9GtW6qE7X5Xl6YCKZDCoXJEWqRofQcoC3GHjqN2/WhJlnaxNVRq3demSO03UNtho57Em5p7o7AOA==", + "license": "MIT", + "dependencies": { + "linkedom": "^0.14.26" + } + }, + "node_modules/@astro-community/astro-embed-youtube": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@astro-community/astro-embed-youtube/-/astro-embed-youtube-0.5.6.tgz", + "integrity": "sha512-/mRfCl/eTBUz0kmjD1psOy0qoDDBorVp0QumUacjFcIkBullYtbeFQ2ZGZ+3N/tA6cR/OIyzr2QA4dQXlY6USg==", + "license": "MIT", + "dependencies": { + "lite-youtube-embed": "^0.3.3" + }, + "peerDependencies": { + "astro": "^2.0.0 || ^3.0.0-beta || ^4.0.0-beta || ^5.0.0-beta" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.4.tgz", + "integrity": "sha512-IOheHwCtpUfvogHHsvu0AbeRZEnjJg3MopdLddkJE70mULItS/Vh37BHcI00mcOJcH1vhD3odbpvWokpxam7xA==", + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.15.0", + "chokidar": "^4.0.1", + "kleur": "^4.1.5", + "yargs": "^17.7.2" + }, + "bin": { + "astro-check": "dist/bin.js" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.10.3.tgz", + "integrity": "sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw==", + "license": "MIT" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.4.1.tgz", + "integrity": "sha512-bMf9jFihO8YP940uD70SI/RDzIhUHJAolWVcO1v5PUivxGKvfLZTLTVVxEYzGYyPsA3ivdLNqMnL5VgmQySa+g==", + "license": "MIT" + }, + "node_modules/@astrojs/language-server": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.15.4.tgz", + "integrity": "sha512-JivzASqTPR2bao9BWsSc/woPHH7OGSGc9aMxXL4U6egVTqBycB3ZHdBJPuOCVtcGLrzdWTosAqVPz1BVoxE0+A==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.10.3", + "@astrojs/yaml2ts": "^0.2.2", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@volar/kit": "~2.4.7", + "@volar/language-core": "~2.4.7", + "@volar/language-server": "~2.4.7", + "@volar/language-service": "~2.4.7", + "fast-glob": "^3.2.12", + "muggle-string": "^0.4.1", + "volar-service-css": "0.0.62", + "volar-service-emmet": "0.0.62", + "volar-service-html": "0.0.62", + "volar-service-prettier": "0.0.62", + "volar-service-typescript": "0.0.62", + "volar-service-typescript-twoslash-queries": "0.0.62", + "volar-service-yaml": "0.0.62", + "vscode-html-languageservice": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-5.3.0.tgz", + "integrity": "sha512-r0Ikqr0e6ozPb5bvhup1qdWnSPUvQu6tub4ZLYaKyG50BXZ0ej6FhGz3GpChKpH7kglRFPObJd/bDyf2VM9pkg==", + "license": "MIT", + "dependencies": { + "@astrojs/prism": "3.1.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.1.0", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.1", + "remark-smartypants": "^3.0.2", + "shiki": "^1.22.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/mdx": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-3.1.9.tgz", + "integrity": "sha512-3jPD4Bff6lIA20RQoonnZkRtZ9T3i0HFm6fcDF7BMsKIZ+xBP2KXzQWiuGu62lrVCmU612N+SQVGl5e0fI+zWg==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "5.3.0", + "@mdx-js/mdx": "^3.1.0", + "acorn": "^8.14.0", + "es-module-lexer": "^1.5.4", + "estree-util-visit": "^2.0.0", + "gray-matter": "^4.0.3", + "hast-util-to-html": "^9.0.3", + "kleur": "^4.1.5", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.0", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.4", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + }, + "peerDependencies": { + "astro": "^4.8.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.1.0.tgz", + "integrity": "sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.29.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + } + }, + "node_modules/@astrojs/react": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-4.1.0.tgz", + "integrity": "sha512-8F0ncvcCexVeQZMwPouLSFuzCK1KXUIYQ57lW3ZG2p7B5DGAajXGanb/CGF7MMSpX8Z0t9sELQqLHOCV/+78Ig==", + "license": "MIT", + "dependencies": { + "@vitejs/plugin-react": "^4.3.4", + "ultrahtml": "^1.5.3", + "vite": "^6.0.1" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=22.0.0" + }, + "peerDependencies": { + "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", + "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", + "react": "^17.0.2 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@astrojs/react/node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/@astrojs/react/node_modules/vite": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", + "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.0", + "postcss": "^8.4.49", + "rollup": "^4.23.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/@astrojs/rss": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.5.tgz", + "integrity": "sha512-IyJVL6z09AQtxbgLaAwebT3T5YKe4oTHDesqydJv1KLHw+zEzzMCFuuNsEyxjiqu7df9+DDCpDXLj/WRiEUXvw==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.2.7", + "kleur": "^4.1.5" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.2.1.tgz", + "integrity": "sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA==", + "license": "MIT", + "dependencies": { + "sitemap": "^8.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^3.23.8" + } + }, + "node_modules/@astrojs/starlight": { + "version": "0.29.2", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.29.2.tgz", + "integrity": "sha512-xv9AhWkP3fxCB6EF6MlT4yEbxzye3aMSbuVbFEGbQh8G/w1MPhdNCnQakIHpmIwwyxwG9cW3mQdAZum4oOO39w==", + "license": "MIT", + "dependencies": { + "@astrojs/mdx": "^3.1.3", + "@astrojs/sitemap": "^3.1.6", + "@pagefind/default-ui": "^1.0.3", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "astro-expressive-code": "^0.38.3", + "bcp-47": "^2.1.0", + "hast-util-from-html": "^2.0.1", + "hast-util-select": "^6.0.2", + "hast-util-to-string": "^3.0.0", + "hastscript": "^9.0.0", + "i18next": "^23.11.5", + "js-yaml": "^4.1.0", + "mdast-util-directive": "^3.0.0", + "mdast-util-to-markdown": "^2.1.0", + "mdast-util-to-string": "^4.0.0", + "pagefind": "^1.0.3", + "rehype": "^13.0.1", + "rehype-format": "^5.0.0", + "remark-directive": "^3.0.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.2" + }, + "peerDependencies": { + "astro": "^4.14.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.1.0.tgz", + "integrity": "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.0.0", + "debug": "^4.3.4", + "dlv": "^1.1.3", + "dset": "^3.1.3", + "is-docker": "^3.0.0", + "is-wsl": "^3.0.0", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.2.tgz", + "integrity": "sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ==", + "license": "MIT", + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==", + "license": "MIT" + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz", + "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.0.tgz", + "integrity": "sha512-z7wkxRSZgrQHXVzObGkXG+Vmj3uRlpM11oCZ9pbaz0nFejvCDmAiNDpY75+wgXOcffKpj4rzGtwGaZxfJKsJxw==", + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "license": "MIT" + }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@expressive-code/core": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.38.3.tgz", + "integrity": "sha512-s0/OtdRpBONwcn23O8nVwDNQqpBGKscysejkeBkwlIeHRLZWgiTVrusT5Idrdz1d8cW5wRk9iGsAIQmwDPXgJg==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.38.3.tgz", + "integrity": "sha512-qL2oC6FplmHNQfZ8ZkTR64/wKo9x0c8uP2WDftR/ydwN/yhe1ed7ZWYb8r3dezxsls+tDokCnN4zYR594jbpvg==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.38.3" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.38.3.tgz", + "integrity": "sha512-kqHnglZeesqG3UKrb6e9Fq5W36AZ05Y9tCREmSN2lw8LVTqENIeCIkLDdWtQ5VoHlKqwUEQFTVlRehdwoY7Gmw==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.38.3", + "shiki": "^1.22.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.38.3.tgz", + "integrity": "sha512-dPK3+BVGTbTmGQGU3Fkj3jZ3OltWUAlxetMHI6limUGCWBCucZiwoZeFM/WmqQa71GyKRzhBT+iEov6kkz2xVA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.38.3" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", + "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.2.0.tgz", + "integrity": "sha512-pHnPL2rm4xbe0LqV376g84hUIsVdy4PK6o2ACveo0DSGoC40eOIwPUPftnUPUinSdDWkkySaL5FT5r9hsXk0ZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.2.0.tgz", + "integrity": "sha512-q2tcnfvcRyx0GnrJoUQJ5bRpiFNtI8DZWM6a4/k8sNJxm2dbM1BnY5hUeo4MbDfpb64Qc1wRMcvBUSOaMKBjfg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/default-ui": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.2.0.tgz", + "integrity": "sha512-MDSbm34veKpzFP5eJMh/pcPdrOc4FZKUsbpDsbdjSLC2ZeuTjsfDBNu9MGZaNUvGKUdlKk5JozQkVO/dzdSxrQ==", + "license": "MIT" + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.2.0.tgz", + "integrity": "sha512-wVtLOlF9AUrwLovP9ZSEKOYnwIVrrxId4I2Mz02Zxm3wbUIJyx8wHf6LyEf7W7mJ6rEjW5jtavKAbngKCAaicg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.2.0.tgz", + "integrity": "sha512-Lo5aO2bA++sQTeEWzK5WKr3KU0yzVH5OnTY88apZfkgL4AVfXckH2mrOU8ouYKCLNPseIYTLFEdj0V5xjHQSwQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.2.0.tgz", + "integrity": "sha512-tGQcwQAb5Ndv7woc7lhH9iAdxOnTNsgCz8sEBbsASPB2A0uI8BWBmVdf2GFLQkYHqnnqYuun63sa+UOzB7Ah3g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", + "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", + "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", + "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", + "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", + "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", + "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", + "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", + "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", + "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", + "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", + "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", + "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", + "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", + "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", + "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", + "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", + "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", + "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", + "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", + "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.24.1.tgz", + "integrity": "sha512-3q/9oarMVcLqJ+NQOdKL40dJVq/UKCsiWXz3QRQPBglHqa8dDJ0p6TuMuk2gHphy5FZcvFtg4UHBgpW0JtZ8+A==", + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "1.24.1", + "@shikijs/engine-oniguruma": "1.24.1", + "@shikijs/types": "1.24.1", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.3" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.24.1.tgz", + "integrity": "sha512-lNgUSHYDYaQ6daj4lJJqcY2Ru9LgHwpFoposJkRVRPh21Yg4kaPFRhzaWoSg3PliwcDOpDuMy3xsmQaJp201Fg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.24.1", + "@shikijs/vscode-textmate": "^9.3.0", + "oniguruma-to-es": "0.7.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.1.tgz", + "integrity": "sha512-KdrTIBIONWd+Xs61eh8HdIpfigtrseat9dpARvaOe2x0g/FNTbwbkGr3y92VSOVD1XotzEskh3v/nCzyWjkf7g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.24.1", + "@shikijs/vscode-textmate": "^9.3.0" + } + }, + "node_modules/@shikijs/types": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.1.tgz", + "integrity": "sha512-ZwZFbShFY/APfKNt3s9Gv8rhTm29GodSKsOW66X6N+HGsZuaHalE1VUEX4fv93UXHTZTLjb3uxn63F96RhGfXw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", + "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", + "license": "MIT" + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/picomatch": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.3.tgz", + "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.1.tgz", + "integrity": "sha512-YW6614BDhqbpR5KtUYzTA+zlA7nayzJRA9ljz9CQoxthR0sDisYZLuvSMsil36t4EH/uAt8T52Xb4sVw17G+SQ==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/@volar/kit": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.10.tgz", + "integrity": "sha512-ul+rLeO9RlFDgkY/FhPWMnpFqAsjvjkKz8VZeOY5YCJMwTblmmSBlNJtFNxSBx9t/k1q80nEthLyxiJ50ZbIAg==", + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.10", + "@volar/typescript": "2.4.10", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.10.tgz", + "integrity": "sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==", + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.10" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.10.tgz", + "integrity": "sha512-odQsgrJh8hOXfxkSj/BSnpjThb2/KDhbxZnG/XAEx6E3QGDQv4hAOz9GWuKoNs0tkjgwphQGIwDMT1JYaTgRJw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.10", + "@volar/language-service": "2.4.10", + "@volar/typescript": "2.4.10", + "path-browserify": "^1.0.1", + "request-light": "^0.7.0", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.10.tgz", + "integrity": "sha512-VxUiWS11rnRzakkqw5x1LPhsz+RBfD0CrrFarLGW2/voliYXEdCuSOM3r8JyNRvMvP4uwhD38ccAdTcULQEAIQ==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.10", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.10.tgz", + "integrity": "sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==", + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.10.tgz", + "integrity": "sha512-F8ZtBMhSXyYKuBfGpYwqA5rsONnOwAVvjyE7KPYJ7wgZqo2roASqNWUnianOomJX5u1cxeRooHV59N0PhvEOgw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.10", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "4.16.17", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.17.tgz", + "integrity": "sha512-OuD+BP7U6OqQLKtZ/FJkU2S+TOlifxS/OKUbZOb5p6y+LLBa1J3zHRJrIl7DUSq6eXY+9wSWwbJpD9JS+lqhxA==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.10.3", + "@astrojs/internal-helpers": "0.4.1", + "@astrojs/markdown-remark": "5.3.0", + "@astrojs/telemetry": "3.1.0", + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/types": "^7.26.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.1.3", + "@types/babel__core": "^7.20.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "boxen": "8.0.1", + "ci-info": "^4.1.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^1.0.1", + "cookie": "^0.7.2", + "cssesc": "^3.0.0", + "debug": "^4.3.7", + "deterministic-object-hash": "^2.0.2", + "devalue": "^5.1.1", + "diff": "^5.2.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "es-module-lexer": "^1.5.4", + "esbuild": "^0.21.5", + "estree-walker": "^3.0.3", + "fast-glob": "^3.3.2", + "flattie": "^1.1.1", + "github-slugger": "^2.0.0", + "gray-matter": "^4.0.3", + "html-escaper": "^3.0.3", + "http-cache-semantics": "^4.1.1", + "js-yaml": "^4.1.0", + "kleur": "^4.1.5", + "magic-string": "^0.30.14", + "magicast": "^0.3.5", + "micromatch": "^4.0.8", + "mrmime": "^2.0.0", + "neotraverse": "^0.6.18", + "ora": "^8.1.1", + "p-limit": "^6.1.0", + "p-queue": "^8.0.1", + "preferred-pm": "^4.0.0", + "prompts": "^2.4.2", + "rehype": "^13.0.2", + "semver": "^7.6.3", + "shiki": "^1.23.1", + "tinyexec": "^0.3.1", + "tsconfck": "^3.1.4", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.3", + "vite": "^5.4.11", + "vitefu": "^1.0.4", + "which-pm": "^3.0.0", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^21.1.1", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.5", + "zod-to-ts": "^1.2.0" + }, + "bin": { + "astro": "astro.js" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "optionalDependencies": { + "sharp": "^0.33.3" + } + }, + "node_modules/astro-expressive-code": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.38.3.tgz", + "integrity": "sha512-Tvdc7RV0G92BbtyEOsfJtXU35w41CkM94fOAzxbQP67Wj5jArfserJ321FO4XA7WG9QMV0GIBmQq77NBIRDzpQ==", + "license": "MIT", + "dependencies": { + "rehype-expressive-code": "^0.38.3" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0" + } + }, + "node_modules/astro-remote": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/astro-remote/-/astro-remote-0.3.2.tgz", + "integrity": "sha512-Xwm6Y+ldQEnDB2l1WwVqeUs3QvUX8LtJWnovpXlf8xhpicPu159jXOhDbHZS9wilGO/+/nR67A1qskF8pDvdGQ==", + "license": "MIT", + "dependencies": { + "entities": "^4.5.0", + "marked": "^12.0.0", + "marked-footnote": "^1.2.2", + "marked-smartypants": "^1.1.6", + "ultrahtml": "^1.5.3" + }, + "engines": { + "node": ">=18.14.1" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" + }, + "node_modules/bcp-47": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", + "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001687", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", + "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "license": "ISC" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-parser": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.0.5.tgz", + "integrity": "sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.1.tgz", + "integrity": "sha512-dbeqFTLYEwlFg7UGtcZhCCG/2WayX72zK3Sq323CEX29CY81tYfVhw1MIdduCtpstB0cTOhJswWlM/OEB3Xp+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "license": "MIT", + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/deterministic-object-hash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", + "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", + "license": "MIT", + "dependencies": { + "base-64": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/devalue": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", + "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "license": "MIT", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", + "license": "(MPL-2.0 OR Apache-2.0)" + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.71", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", + "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", + "license": "ISC" + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "license": "EPL-2.0" + }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "license": "MIT" + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/expressive-code": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.38.3.tgz", + "integrity": "sha512-COM04AiUotHCKJgWdn7NtW2lqu8OW8owAidMpkXt1qxrZ9Q2iC7+tok/1qIn2ocGnczvr9paIySgGnEwFeEQ8Q==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.38.3", + "@expressive-code/plugin-frames": "^0.38.3", + "@expressive-code/plugin-shiki": "^0.38.3", + "@expressive-code/plugin-text-markers": "^0.38.3" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-yarn-workspace-root2": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", + "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2", + "pkg-dir": "^4.2.0" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/framer-motion": { + "version": "11.14.4", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.14.4.tgz", + "integrity": "sha512-NQuzr9JbeJDMQmy0FFLhLzk9h1kAjVC1tGE/HY4ubF02B95EBm2lpA21LE3Od/OpXqXgp0zl5Hdqu25hliBRsA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.14.3", + "motion-utils": "^11.14.3", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz", + "integrity": "sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.3.tgz", + "integrity": "sha512-OVRQlQ1XuuLP8aFVLYmC2atrfWHS5UD3shonxpnyrjcCkwtvmt/+N6kYJdcY4mkMJhxp4kj2EFIxQ9kvkkt/eQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree/node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "license": "MIT" + }, + "node_modules/hast-util-to-estree/node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", + "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.0.tgz", + "integrity": "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause" + }, + "node_modules/i18next": { + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-absolute-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "license": "MIT" + }, + "node_modules/katex": { + "version": "0.16.22", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", + "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/linkedom": { + "version": "0.14.26", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.26.tgz", + "integrity": "sha512-mK6TrydfFA7phrnp+1j57ycBwFI5bGSW6YXlw9acHoqF+mP/y+FooEYYyniOt5Ot57FSKB3iwmnuQ1UUyNLm5A==", + "license": "ISC", + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^8.0.1", + "uhyphen": "^0.2.0" + } + }, + "node_modules/lite-youtube-embed": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lite-youtube-embed/-/lite-youtube-embed-0.3.3.tgz", + "integrity": "sha512-gFfVVnj6NRjxVfJKo3qoLtpi0v5mn3AcR4eKD45wrxQuxzveFJUb+7Cr6uV6n+DjO8X3p0UzPPquhGt0H/y+NA==", + "license": "Apache-2.0" + }, + "node_modules/load-yaml-file": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz", + "integrity": "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.13.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/load-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/load-yaml-file/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.14", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz", + "integrity": "sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-footnote": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/marked-footnote/-/marked-footnote-1.2.4.tgz", + "integrity": "sha512-DB2Kl+wFh6YwZd70qABMY6WUkG1UuyqoNTFoDfGyG79Pz24neYtLBkB+45a7o72V7gkfvbC3CGzIYFobxfMT1Q==", + "license": "MIT", + "peerDependencies": { + "marked": ">=7.0.0" + } + }, + "node_modules/marked-plaintify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/marked-plaintify/-/marked-plaintify-1.0.1.tgz", + "integrity": "sha512-KQhxtuVWf3Ij3YMiW4ArlgNOVmzOAlP0o/upsu2+h7Q4TCAwG4UvkYTteZF2sDDomXQnNSLmfyAhoR0gx2Orgw==", + "license": "MIT", + "peerDependencies": { + "marked": ">=7.0.0" + } + }, + "node_modules/marked-smartypants": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/marked-smartypants/-/marked-smartypants-1.1.9.tgz", + "integrity": "sha512-VPeuaUr5IWptI7nJdgQ9ugrLWYGv13NdzEXTtKY3cmB4aRWOI2RzhLlf+xQp6Wnob9SAPO2sNVlfSJr+nflk/A==", + "license": "MIT", + "dependencies": { + "smartypants": "^0.2.2" + }, + "peerDependencies": { + "marked": ">=4 <16" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", + "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "10.9.3", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.3.tgz", + "integrity": "sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.28.1", + "cytoscape-cose-bilkent": "^4.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5 <3.1.7", + "elkjs": "^0.9.0", + "katex": "^0.16.9", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/mermaid/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/mermaid/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/mermaid/node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid/node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid/node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mermaid/node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mermaid/node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mermaid/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mermaid/node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", + "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.1.tgz", + "integrity": "sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==", + "license": "MIT", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.2.tgz", + "integrity": "sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", + "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/motion": { + "version": "11.14.4", + "resolved": "https://registry.npmjs.org/motion/-/motion-11.14.4.tgz", + "integrity": "sha512-ZIaw6ko88B8rSmBEFzqbTCQMbo9xMu8f4PSXSGdb9DTDy8R0sXcbwMEKmTEYkrj9TmZ4n+Ebd0KYjtqHgzRkRQ==", + "license": "MIT", + "dependencies": { + "framer-motion": "^11.14.4", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "11.14.3", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.14.3.tgz", + "integrity": "sha512-lW+D2wBy5vxLJi6aCP0xyxTxlTfiu+b+zcpVbGVFUxotwThqhdpPRSmX8xztAgtZMPMeU0WGVn/k1w4I+TbPqA==", + "license": "MIT" + }, + "node_modules/motion-utils": { + "version": "11.14.3", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.14.3.tgz", + "integrity": "sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ==", + "license": "MIT" + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==", + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oniguruma-to-es": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.7.0.tgz", + "integrity": "sha512-HRaRh09cE0gRS3+wi2zxekB+I5L8C/gN60S+vb11eADHUaB/q4u8wGGOX3GvwvitG8ixaeycZfeoyruKQzUgNg==", + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^5.0.2", + "regex-recursion": "^4.3.0" + } + }, + "node_modules/ora": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.1.tgz", + "integrity": "sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.1.0.tgz", + "integrity": "sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz", + "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.3.tgz", + "integrity": "sha512-UJUyfKbwvr/uZSV6btANfb+0t/mOhKV/KXcCUTp8FcQI+v/0d+wXqH4htrW0E4rR6WiEO/EPvUFiV9D5OI4vlw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pagefind": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.2.0.tgz", + "integrity": "sha512-sFVv5/x73qCp9KlLHv8/uWDv7rG1tsWcG9MuXc5YTrXIrb8c1Gshm9oc5rMLXNZILXUWai8WczqaK4jjroEzng==", + "license": "MIT", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.2.0", + "@pagefind/darwin-x64": "1.2.0", + "@pagefind/linux-arm64": "1.2.0", + "@pagefind/linux-x64": "1.2.0", + "@pagefind/windows-x64": "1.2.0" + } + }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/preferred-pm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.0.0.tgz", + "integrity": "sha512-gYBeFTZLu055D8Vv3cSPox/0iTPtkzxpLroSYYA7WXgRi31WCJ51Uyl8ZiPeUUjyvs2MBzK+S8v9JVUgHU/Sqw==", + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "find-yarn-workspace-root2": "1.2.16", + "which-pm": "^3.0.0" + }, + "engines": { + "node": ">=18.12" + } + }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.0.tgz", + "integrity": "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regex": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/regex/-/regex-5.0.2.tgz", + "integrity": "sha512-/pczGbKIQgfTMRV0XjABvc5RzLqQmwqxLHdQao2RTXPk+pmTXB2P0IaUHYdYyk412YLwUIkaeMd5T+RzVgTqnQ==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-4.3.0.tgz", + "integrity": "sha512-5LcLnizwjcQ2ALfOj95MjcatxyqF5RPySx9yT+PaXu3Gox2vyAtLDjHB8NTJLtMGkvyau6nI3CfpwFCjPUIs/A==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-expressive-code": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.38.3.tgz", + "integrity": "sha512-RYSSDkMBikoTbycZPkcWp6ELneANT4eTpND1DSRJ6nI2eVFUwTBDCvE2vO6jOOTaavwnPiydi4i/87NRyjpdOA==", + "license": "MIT", + "dependencies": { + "expressive-code": "^0.38.3" + } + }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", + "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz", + "integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", + "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.28.1", + "@rollup/rollup-android-arm64": "4.28.1", + "@rollup/rollup-darwin-arm64": "4.28.1", + "@rollup/rollup-darwin-x64": "4.28.1", + "@rollup/rollup-freebsd-arm64": "4.28.1", + "@rollup/rollup-freebsd-x64": "4.28.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", + "@rollup/rollup-linux-arm-musleabihf": "4.28.1", + "@rollup/rollup-linux-arm64-gnu": "4.28.1", + "@rollup/rollup-linux-arm64-musl": "4.28.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", + "@rollup/rollup-linux-riscv64-gnu": "4.28.1", + "@rollup/rollup-linux-s390x-gnu": "4.28.1", + "@rollup/rollup-linux-x64-gnu": "4.28.1", + "@rollup/rollup-linux-x64-musl": "4.28.1", + "@rollup/rollup-win32-arm64-msvc": "4.28.1", + "@rollup/rollup-win32-ia32-msvc": "4.28.1", + "@rollup/rollup-win32-x64-msvc": "4.28.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/shiki": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.24.1.tgz", + "integrity": "sha512-/qByWMg05+POb63c/OvnrU17FcCUa34WU4F6FCrd/mjDPEDPl8YUNRkRMbo8l3iYMLydfCgxi1r37JFoSw8A4A==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "1.24.1", + "@shikijs/engine-javascript": "1.24.1", + "@shikijs/engine-oniguruma": "1.24.1", + "@shikijs/types": "1.24.1", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.0.tgz", + "integrity": "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, + "node_modules/smartypants": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/smartypants/-/smartypants-0.2.2.tgz", + "integrity": "sha512-TzobUYoEft/xBtb2voRPryAUIvYguG0V7Tt3de79I1WfXgCwelqVsGuZSnu3GFGRZhXR90AeEYIM+icuB/S06Q==", + "license": "BSD-3-Clause", + "bin": { + "smartypants": "bin/smartypants.js", + "smartypantsu": "bin/smartypantsu.js" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/starlight-blog": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/starlight-blog/-/starlight-blog-0.15.0.tgz", + "integrity": "sha512-zNs8Z6eJAqurarD3up8kosfAXNDiZEaweI7S7vRfeTN6eOZijPa3QVifJbYK8n6sdX4W3liEqPkDF+PIz3TxxQ==", + "license": "MIT", + "dependencies": { + "@astrojs/mdx": "3.1.9", + "@astrojs/rss": "4.0.5", + "astro-remote": "0.3.2", + "github-slugger": "2.0.0", + "marked": "12.0.2", + "marked-plaintify": "1.0.1", + "ultrahtml": "1.5.3" + }, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.28.3" + } + }, + "node_modules/starlight-image-zoom": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/starlight-image-zoom/-/starlight-image-zoom-0.9.0.tgz", + "integrity": "sha512-XG87T80g5hsT6dvtNk9OKx0gD+M8lsloVTApQYnxdc3JD8lQBfu2kCsrwkyrwXZRtV7JRyd0PDHwx1UFmGmI1g==", + "license": "MIT", + "dependencies": { + "rehype-raw": "7.0.0", + "unist-util-visit": "5.0.0", + "unist-util-visit-parents": "6.0.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.22.0" + } + }, + "node_modules/starlight-links-validator": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.13.4.tgz", + "integrity": "sha512-LdmLbJyPHVrSUhcuxiP3pJNnW8zRcOg/32C996Ic0LOCKbB8vylqHLvAMdIhT67FvEV4eAROun+2wTVU2J156A==", + "license": "MIT", + "dependencies": { + "@types/picomatch": "2.3.3", + "github-slugger": "2.0.0", + "hast-util-from-html": "2.0.1", + "hast-util-has-property": "3.0.0", + "is-absolute-url": "4.0.1", + "kleur": "4.1.5", + "mdast-util-to-string": "4.0.0", + "picomatch": "4.0.2", + "unist-util-visit": "5.0.0" + }, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.15.0", + "astro": ">=4.0.0" + } + }, + "node_modules/starlight-links-validator/node_modules/hast-util-from-html": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz", + "integrity": "sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/starlight-showcases": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/starlight-showcases/-/starlight-showcases-0.2.0.tgz", + "integrity": "sha512-YWJuTqArkUdVJV85VKZJ0BvKCQRu1SKtH/Cr5t6G/oIfI4IptWc92E7BmiuNnpuQ2U7TczTRidCYurPrbgQQVA==", + "license": "MIT", + "dependencies": { + "@astro-community/astro-embed-twitter": "^0.5.4", + "@astro-community/astro-embed-youtube": "^0.5.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.23.0" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tsconfck": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz", + "integrity": "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.0.tgz", + "integrity": "sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.5.tgz", + "integrity": "sha512-fAIveQKsoYj55CozUiBoj4b/7WpN0i4o74wiGY5JVUEoD0XiqDk1tJqTEjgzL2/AizKQrXxyRosSebyDzBZKjw==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "license": "ISC" + }, + "node_modules/ultrahtml": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.3.tgz", + "integrity": "sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.4.tgz", + "integrity": "sha512-y6zEE3PQf6uu/Mt6DTJ9ih+kyJLr4XcSgHR2zUkM8SWDhuixEJxfJ6CZGMHh1Ec3vPLoEA0IHU5oWzVqw8ulow==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/volar-service-css": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.62.tgz", + "integrity": "sha512-JwNyKsH3F8PuzZYuqPf+2e+4CTU8YoyUHEHVnoXNlrLe7wy9U3biomZ56llN69Ris7TTy/+DEX41yVxQpM4qvg==", + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.62.tgz", + "integrity": "sha512-U4dxWDBWz7Pi4plpbXf4J4Z/ss6kBO3TYrACxWNsE29abu75QzVS0paxDDhI6bhqpbDFXlpsDhZ9aXVFpnfGRQ==", + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.0", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.62.tgz", + "integrity": "sha512-Zw01aJsZRh4GTGUjveyfEzEqpULQUdQH79KNEiKVYHZyuGtdBRYCHlrus1sueSNMxwwkuF5WnOHfvBzafs8yyQ==", + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.62.tgz", + "integrity": "sha512-h2yk1RqRTE+vkYZaI9KYuwpDfOQRrTEMvoHol0yW4GFKc75wWQRrb5n/5abDrzMPrkQbSip8JH2AXbvrRtYh4w==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.62.tgz", + "integrity": "sha512-p7MPi71q7KOsH0eAbZwPBiKPp9B2+qrdHAd6VY5oTo9BUXatsOAdakTm9Yf0DUj6uWBAaOT01BSeVOPwucMV1g==", + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.3", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.62.tgz", + "integrity": "sha512-KxFt4zydyJYYI0kFAcWPTh4u0Ha36TASPZkAnNY784GtgajerUqM80nX/W1d0wVhmcOFfAxkVsf/Ed+tiYU7ng==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.62.tgz", + "integrity": "sha512-k7gvv7sk3wa+nGll3MaSKyjwQsJjIGCHFjVkl3wjaSP2nouKyn9aokGmqjrl39mi88Oy49giog2GkZH526wjig==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.15.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz", + "integrity": "sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/vscode-html-languageservice": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz", + "integrity": "sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "license": "Apache-2.0" + }, + "node_modules/which-pm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.0.tgz", + "integrity": "sha512-ysVYmw6+ZBhx3+ZkcPwRuJi38ZOTLJJ33PSHaitLxSKUMsh0LkKd0nC69zZCwt5D+AYUcMK2hhw4yWny20vSGg==", + "license": "MIT", + "dependencies": { + "load-yaml-file": "^0.2.0" + }, + "engines": { + "node": ">=18.12" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yaml-language-server": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.15.0.tgz", + "integrity": "sha512-N47AqBDCMQmh6mBLmI6oqxryHRzi33aPFPsJhYy3VTUGCdLHYjGh4FZzpUjRlphaADBBkDmnkM/++KNIOHi5Rw==", + "license": "MIT", + "dependencies": { + "ajv": "^8.11.0", + "lodash": "4.17.21", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2", + "yaml": "2.2.2" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + }, + "optionalDependencies": { + "prettier": "2.8.7" + } + }, + "node_modules/yaml-language-server/node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-jsonrpc": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz", + "integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.16.0" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver-types": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "license": "ISC", + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz", + "integrity": "sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.23.3" + } + }, + "node_modules/zod-to-ts": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", + "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", + "peerDependencies": { + "typescript": "^4.9.4 || ^5.0.2", + "zod": "^3" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..5d10c8ed3 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,31 @@ +{ + "name": "wails-docs", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/check": "0.9.4", + "@astrojs/react": "4.1.0", + "@astrojs/starlight": "0.29.2", + "@types/react": "19.0.1", + "@types/react-dom": "19.0.2", + "astro": "4.16.17", + "framer-motion": "11.14.4", + "mermaid": "^10.9.3", + "motion": "11.14.4", + "react": "19.0.0", + "react-dom": "19.0.0", + "sharp": "0.33.5", + "starlight-blog": "0.15.0", + "starlight-image-zoom": "0.9.0", + "starlight-links-validator": "0.13.4", + "starlight-showcases": "0.2.0", + "typescript": "5.7.2" + } +} diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 000000000..e682e4453 --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/missing.png b/docs/public/missing.png new file mode 100644 index 000000000..05d2bafc3 Binary files /dev/null and b/docs/public/missing.png differ diff --git a/docs/public/sponsors/jetbrains-grayscale.webp b/docs/public/sponsors/jetbrains-grayscale.webp new file mode 100644 index 000000000..be39c4856 Binary files /dev/null and b/docs/public/sponsors/jetbrains-grayscale.webp differ diff --git a/docs/public/sponsors/sponsors.svg b/docs/public/sponsors/sponsors.svg new file mode 100644 index 000000000..1a9985e59 --- /dev/null +++ b/docs/public/sponsors/sponsors.svg @@ -0,0 +1,197 @@ + + + + +Champion + Masato Miura + +Bronze Sponsors + Cody Bentley + + + + Kazuya Gokita + + + + Simon Thomas + + + + CodeRabbit + +Covering Costs + Nick + + + + Marcus + + + + John + + + + Matt Holt + + + + Iain + + + + Julien + + + + Andrei + + + + Michael + +Buying Breakfast + tc-hib + + + + Tai Groot + + + + Tom Wu + + + + Arden + + + + igops + + + + vaaski + +Buying Coffee + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Helpers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/sponsors/zsa.png b/docs/public/sponsors/zsa.png new file mode 100644 index 000000000..507e983fa Binary files /dev/null and b/docs/public/sponsors/zsa.png differ diff --git a/docs/src/assets/blog-images/browser.webp b/docs/src/assets/blog-images/browser.webp new file mode 100644 index 000000000..a19d5b036 Binary files /dev/null and b/docs/src/assets/blog-images/browser.webp differ diff --git a/docs/src/assets/blog-images/build-cross-windows.webp b/docs/src/assets/blog-images/build-cross-windows.webp new file mode 100644 index 000000000..ba1a538f6 Binary files /dev/null and b/docs/src/assets/blog-images/build-cross-windows.webp differ diff --git a/docs/src/assets/blog-images/build-darwin-amd.webp b/docs/src/assets/blog-images/build-darwin-amd.webp new file mode 100644 index 000000000..6126129ca Binary files /dev/null and b/docs/src/assets/blog-images/build-darwin-amd.webp differ diff --git a/docs/src/assets/blog-images/build-darwin-arm.webp b/docs/src/assets/blog-images/build-darwin-arm.webp new file mode 100644 index 000000000..47fcf4583 Binary files /dev/null and b/docs/src/assets/blog-images/build-darwin-arm.webp differ diff --git a/docs/src/assets/blog-images/build-darwin-universal.webp b/docs/src/assets/blog-images/build-darwin-universal.webp new file mode 100644 index 000000000..c95d92ccb Binary files /dev/null and b/docs/src/assets/blog-images/build-darwin-universal.webp differ diff --git a/docs/src/assets/blog-images/devtools.png b/docs/src/assets/blog-images/devtools.png new file mode 100644 index 000000000..e56a0d304 Binary files /dev/null and b/docs/src/assets/blog-images/devtools.png differ diff --git a/docs/src/assets/blog-images/linux-build-cross-windows.webp b/docs/src/assets/blog-images/linux-build-cross-windows.webp new file mode 100644 index 000000000..cbed7585b Binary files /dev/null and b/docs/src/assets/blog-images/linux-build-cross-windows.webp differ diff --git a/docs/src/assets/blog-images/montage.png b/docs/src/assets/blog-images/montage.png new file mode 100644 index 000000000..ddd771851 Binary files /dev/null and b/docs/src/assets/blog-images/montage.png differ diff --git a/docs/src/assets/blog-images/multiwindow.webp b/docs/src/assets/blog-images/multiwindow.webp new file mode 100644 index 000000000..746a1d7f2 Binary files /dev/null and b/docs/src/assets/blog-images/multiwindow.webp differ diff --git a/docs/src/assets/blog-images/remote-linux.webp b/docs/src/assets/blog-images/remote-linux.webp new file mode 100644 index 000000000..25ad11ea3 Binary files /dev/null and b/docs/src/assets/blog-images/remote-linux.webp differ diff --git a/docs/src/assets/blog-images/remote-mac.webp b/docs/src/assets/blog-images/remote-mac.webp new file mode 100644 index 000000000..bf4758e1a Binary files /dev/null and b/docs/src/assets/blog-images/remote-mac.webp differ diff --git a/docs/src/assets/blog-images/remote.webp b/docs/src/assets/blog-images/remote.webp new file mode 100644 index 000000000..3968c5ddc Binary files /dev/null and b/docs/src/assets/blog-images/remote.webp differ diff --git a/docs/src/assets/blog-images/vscode.webp b/docs/src/assets/blog-images/vscode.webp new file mode 100644 index 000000000..156fa3078 Binary files /dev/null and b/docs/src/assets/blog-images/vscode.webp differ diff --git a/docs/src/assets/blog-images/wails-linux.webp b/docs/src/assets/blog-images/wails-linux.webp new file mode 100644 index 000000000..45c76ed0e Binary files /dev/null and b/docs/src/assets/blog-images/wails-linux.webp differ diff --git a/docs/src/assets/blog-images/wails-mac.webp b/docs/src/assets/blog-images/wails-mac.webp new file mode 100644 index 000000000..26ee0be13 Binary files /dev/null and b/docs/src/assets/blog-images/wails-mac.webp differ diff --git a/docs/src/assets/blog-images/wails-menus-linux.webp b/docs/src/assets/blog-images/wails-menus-linux.webp new file mode 100644 index 000000000..391225a35 Binary files /dev/null and b/docs/src/assets/blog-images/wails-menus-linux.webp differ diff --git a/docs/src/assets/blog-images/wails-menus-mac.webp b/docs/src/assets/blog-images/wails-menus-mac.webp new file mode 100644 index 000000000..033f68d35 Binary files /dev/null and b/docs/src/assets/blog-images/wails-menus-mac.webp differ diff --git a/docs/src/assets/blog-images/wails-menus.webp b/docs/src/assets/blog-images/wails-menus.webp new file mode 100644 index 000000000..ba64152b4 Binary files /dev/null and b/docs/src/assets/blog-images/wails-menus.webp differ diff --git a/docs/src/assets/blog-images/wails.webp b/docs/src/assets/blog-images/wails.webp new file mode 100644 index 000000000..777f4d05c Binary files /dev/null and b/docs/src/assets/blog-images/wails.webp differ diff --git a/docs/src/assets/contributors.html b/docs/src/assets/contributors.html new file mode 100644 index 000000000..465a64801 --- /dev/null +++ b/docs/src/assets/contributors.html @@ -0,0 +1,246 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lea Anthony
Lea Anthony

💻 🤔 🎨 🖋 💡 🧑‍🏫 📆 🔧 🐛 📝 🚧 📦 👀 💬 🔬 ⚠️ 📢 👀 📖
stffabi
stffabi

💻 🤔 🎨 🐛 🚧 📦 👀 💬 🔬 👀 📖 ⚠️
Travis McLane
Travis McLane

💻 🔬 📦 🤔 🐛 👀 ⚠️ 💬 📖
Misite Bao
Misite Bao

📖 🌍 🔬 🚧
Byron Chris
Byron Chris

💻 🔬 🚧 🐛 👀 ⚠️ 💬 🤔 🎨 📦 🚇
konez2k
konez2k

💻 📦 🤔
Dario Emerson
Dario Emerson

💻 🐛 🤔 ⚠️
Ian M. Jones
Ian M. Jones

💻 🐛 🤔 ⚠️ 👀 📦
marktohark
marktohark

💻
Ryan H
Ryan H

💻
Cody Bentley
Cody Bentley

💻 📦 🤔 💵
Florent
Florent

💻 🐛
Alexander Hudek
Alexander Hudek

💻 💵
Tim Kipp
Tim Kipp

💻
Altynbek Kaliakbarov
Altynbek Kaliakbarov

💻
Nikolai Zimmermann
Nikolai Zimmermann

💻
k-muchmore
k-muchmore

💻
Snider
Snider

💻 🤔 📖 💵
Albert Sun
Albert Sun

💻 ⚠️
Ariel
Ariel

💻 🐛
Ilgıt Yıldırım
Ilgıt Yıldırım

💻 🐛 💵
Toyam Cox
Toyam Cox

💻 📦 🐛
hi019
hi019

💻 🐛
Arthur Wiebe
Arthur Wiebe

💻 🐛
Balakrishna Prasad Ganne
Balakrishna Prasad Ganne

💻
BillBuilt
BillBuilt

💻 📦 🤔 💬 💵
Eng Zer Jun
Eng Zer Jun

🚧 💻
LGiki
LGiki

📖
Lontten
Lontten

📖
Lukas Crepaz
Lukas Crepaz

💻 🐛
Marcus Crane
Marcus Crane

🐛 📖 💵
Qais Patankar
Qais Patankar

📖
Wakeful-Cloud
Wakeful-Cloud

💻 🐛
Zámbó, Levente
Zámbó, Levente

💻 📦 🐛 ⚠️
Ironpark
Ironpark

💻 🤔
mondy
mondy

💻 📖
Benjamin Ryan
Benjamin Ryan

🐛
fallendusk
fallendusk

📦 💻
Mat Ryer
Mat Ryer

💻 🤔 🐛
Abtin
Abtin

💻 🐛
Adrian Lanzafame
Adrian Lanzafame

📦 💻
Aleksey Polyakov
Aleksey Polyakov

🐛 💻
Alexander Matviychuk
Alexander Matviychuk

💻 📦
AlienRecall
AlienRecall

💻 📦
Aman
Aman

📖
Amaury Tobias Quiroz
Amaury Tobias Quiroz

💻 🐛
Andreas Wenk
Andreas Wenk

📖
Antonio Stanković
Antonio Stanković

💻 📦
Arpit Jain
Arpit Jain

📖
Austin Schey
Austin Schey

💻 🐛
Benjamin Thomas
Benjamin Thomas

💻 📦 🤔
Bertram Truong
Bertram Truong

💻 🐛
Blake Bourque
Blake Bourque

📖
Denis
Denis

📖
diogox
diogox

💻 📦
Dmitry Gomzyakov
Dmitry Gomzyakov

💻 📦
Edward Browncross
Edward Browncross

💻
Elie Grenon
Elie Grenon

💻
Florian Didron
Florian Didron

💻 🐛 🤔 ⚠️ 👀 📦
GargantuaX
GargantuaX

💵
Igor Minin
Igor Minin

💻 🐛
Jae-Sung Lee
Jae-Sung Lee

💻 🤔
Jarek
Jarek

💻 📦
Junker
Junker

📖
Kris Raney
Kris Raney

💻 🐛
Luken
Luken

📖
Mark Stenglein
Mark Stenglein

💻 🐛
buddyabaddon
buddyabaddon

💻
MikeSchaap
MikeSchaap

💻 🐛
NYSSEN Michaël
NYSSEN Michaël

💻 🐛
Nan0
Nan0

💻 🤔 ⚠️ 👀
oskar
oskar

📖
Pierre Joye
Pierre Joye

💻 🐛 🤔 ⚠️
Reuben Thomas-Davis
Reuben Thomas-Davis

💻 🐛
Robin
Robin

💻 🐛
Sebastian Bauer
Sebastian Bauer

💻 🤔 ⚠️ 👀 💬
Sidharth Rathi
Sidharth Rathi

📖 🐛
Sithembiso Khumalo
Sithembiso Khumalo

💻 🐛
Soheib El-Harrache
Soheib El-Harrache

💻 🐛 💵
Sophie Au
Sophie Au

💻 🐛
Stefanos Papadakis
Stefanos Papadakis

💻 🐛
Steve Chung
Steve Chung

💻 🐛
Timm Ortloff
Timm Ortloff

📖
Tom
Tom

💻
Valentin Trinqué
Valentin Trinqué

💻 🐛
mattn
mattn

💻 🐛
bearsh
bearsh

💻 🤔 📖
chenxiao
chenxiao

💻 🤔 📖
fengweiqiang
fengweiqiang

💻 📦
flin7
flin7

📖
fred21O4
fred21O4

📖
gardc
gardc

📖
rayshoo
rayshoo

📖
Ishiyama Yuzuki
Ishiyama Yuzuki

💻 🐛
佰阅
佰阅

💻
刀刀
刀刀

📖 🐛
归位
归位

💻 🐛
skamensky
skamensky

💻 🤔 📖
dependabot[bot]
dependabot[bot]

💻 🚧
Damian Sieradzki
Damian Sieradzki

💵
John Dorman
John Dorman

💵
Ian Sinnott
Ian Sinnott

💵
Arden Shackelford
Arden Shackelford

💵
Bironou
Bironou

💵
CharlieGo_
CharlieGo_

💵
overnet
overnet

💵
jugglingjsons
jugglingjsons

💵
Selvin Ortiz
Selvin Ortiz

💵
ZanderCodes
ZanderCodes

💵
Michael Voronov
Michael Voronov

💵
letheanVPN
letheanVPN

💵
Tai Groot
Tai Groot

💵
easy-web-it
easy-web-it

💵
Michael Olson
Michael Olson

💵
EdenNetwork Italia
EdenNetwork Italia

💵
ondoki
ondoki

💵
QuEST Rail LLC
QuEST Rail LLC

💵
Gilgameš
Gilgameš

💵
Bernt-Johan Bergshaven
Bernt-Johan Bergshaven

💵
Liam Bigelow
Liam Bigelow

💵
Nick Arellano
Nick Arellano

💵
Frank Chiarulli Jr.
Frank Chiarulli Jr.

💵
Tyler
Tyler

💵
Trea Hauet
Trea Hauet

💵
Kent 'picat' Gruber
Kent 'picat' Gruber

💵
tc-hib
tc-hib

💵
Antonio
Antonio

📖
MyNameIsAres
MyNameIsAres

📖
Maicarons J
Maicarons J

📖
kiddov
kiddov

📖 💵 ⚠️ 🤔
Nicolas Coutin
Nicolas Coutin

💵
Parvin Eyvazov
Parvin Eyvazov

📖
github-actions[bot]
github-actions[bot]

💻
Oleg Gulevskyy
Oleg Gulevskyy

💻 📖 🚧 📦
Richard Guay
Richard Guay

📖
Adam Tenderholt
Adam Tenderholt

💻
JulioDRF
JulioDRF

💻
Scott Opell
Scott Opell

💻
Vadim Shchepotev
Vadim Shchepotev

💻
Will Andrews
Will Andrews

💻
Gwyn
Gwyn

💻 👀 💬 🔬
希嘉嘉
希嘉嘉

💻
ALMAS
ALMAS

💻
Alex
Alex

💻
Arif Ali
Arif Ali

💻
Artur Siarohau
Artur Siarohau

💻
Binyamin Aron Green
Binyamin Aron Green

💻
Brian Dwyer
Brian Dwyer

💻
Christian Kilb
Christian Kilb

💻
David Florness
David Florness

📖
David Walton
David Walton

💻
Debdut Karmakar
Debdut Karmakar

💻
Dieter Zhu
Dieter Zhu

💻
Fredrik Holmqvist
Fredrik Holmqvist

💻
Giovanni Palma
Giovanni Palma

💻
Hao
Hao

💻
Igor Sementsov
Igor Sementsov

💻
Johannes Haseitl
Johannes Haseitl

💻
Joshua Hull
Joshua Hull

💻
Joshua Mangiola
Joshua Mangiola

📖
Kevin MacMartin
Kevin MacMartin

💻
Liang Li
Liang Li

💻
Marvin Collins Hosea
Marvin Collins Hosea

💻
Matt Holt
Matt Holt

💻
Niklas
Niklas

💻
Andy Hsu
Andy Hsu

💻
NullCode
NullCode

💻
Oussama Sethoum
Oussama Sethoum

💻
ParkourLiu
ParkourLiu

💻
Rachel Chen
Rachel Chen

💻
Rob Nice
Rob Nice

💻
Ryo TAGAMI
Ryo TAGAMI

💻
Sam Hennessy
Sam Hennessy

💻
Sean
Sean

💻
Sean Gosiaco
Sean Gosiaco

💻
Eric P Sheets
Eric P Sheets

💻
Supian M
Supian M

💻
Watson-Sei
Watson-Sei

💻 📖
Yuki Shindo
Yuki Shindo

💻
cuigege
cuigege

💻
cybertramp
cybertramp

💻
hiroki yagi
hiroki yagi

💻
imgbot[bot]
imgbot[bot]

💻
juju
juju

💻
Michael Eatherly
Michael Eatherly

💻
tk
tk

💻
allcontributors[bot]
allcontributors[bot]

📖
wander
wander

📖
+ + +
\ No newline at end of file diff --git a/docs/src/assets/menus/context-menu.png b/docs/src/assets/menus/context-menu.png new file mode 100644 index 000000000..dd43c0c7f --- /dev/null +++ b/docs/src/assets/menus/context-menu.png @@ -0,0 +1 @@ +iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTIzODU1NjI4RjU1MTFFQjg5QzVCNTY1QjY1NjY1QjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTIzODU1NjM4RjU1MTFFQjg5QzVCNTY1QjY1NjY1QjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMjM4NTU2MDhGNTUxMUVCODlDNUI1NjVCNjU2NjVCNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxMjM4NTU2MThGNTUxMUVCODlDNUI1NjVCNjU2NjVCNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAAALAAAAAAsASwBAAL/hI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMKpfMpvMJjUqn1Kr1is1qt9yu9wsOi8fksvmMTqvX7Lb7DY/L5/S6/Y7P6/f8vv8PGCg4SFhoeIiYqLjI2Oj4CBkpOUlZaXmJmam5ydnp+QkaKjpKWmp6ipqqusra6voKGys7S1tre4ubq7vL2+v7CxwsPExcbHyMnKy8zNzs/AwdLT1NXW19jZ2tvc3d7f0NHi4+Tl5ufo6err7O3u7+Dh8vP09fb3+Pn6+/z9/v/w8woMCBBAsaPIgwocKFDBs6fAgxosSJFCtavIgxo8aN/xw7evwIMqTIkSRLmjyJMqXKlSxbunwJM6bMmTRr2ryJM6fOnTx7+vwJNKjQoUSLGj2KNKnSpUybOn0KNarUqVSrWr2KNavWrVy7ev0KNqzYsWTLmj2LNq3atWzbun0LN67cuXTr2r2LN6/evXz7+v0LOLDgwYQLGz6MOLHixYwbO34MObLkyZQrW76MObPmzZw7e/4MOrTo0aRLmz6NOrXq1axbu34NO7bs2bRr276NO7fu3bx7+/4NPLjw4cSLGz+OPLny5cybO38OPbr06dSrW7+OPbv27dy7e/8OPrz48eTLmz+PPr369ezbu38PP778+fTr27+PP7/+/fz7+9OPX7//fwAGKOCABBZo4IEIJqjgggw26OCDEEYo4YQUVmjhhRhmqOGGHHbo4YcghijiiCSWaOKJKKao4oostujiizDGKOOMNNZo44045qjjjjz26OOPQAYp5JBEFmnkkUgmqeSSTDbp5JNQRinllFRWaeWVWGap5ZZcdunll2CGKeaYZJZp5plopqnmmmy26eabcMYp55x01mnnnXjmqeeefPbp55+ABirooIQWauihiCaq6KKMNuroo5BGKumklFZq6aWYZqrpppx26umnoIYq6qiklmrqqaimquqqrLbq6quwxirrqxEAADs= diff --git a/docs/src/assets/qr1.png b/docs/src/assets/qr1.png new file mode 100644 index 000000000..69885acbe Binary files /dev/null and b/docs/src/assets/qr1.png differ diff --git a/docs/src/assets/qr2.png b/docs/src/assets/qr2.png new file mode 100644 index 000000000..e2ce2a5f1 Binary files /dev/null and b/docs/src/assets/qr2.png differ diff --git a/docs/src/assets/showcase-images/bboard.webp b/docs/src/assets/showcase-images/bboard.webp new file mode 100644 index 000000000..463d25de0 Binary files /dev/null and b/docs/src/assets/showcase-images/bboard.webp differ diff --git a/docs/src/assets/showcase-images/cfntracker.webp b/docs/src/assets/showcase-images/cfntracker.webp new file mode 100644 index 000000000..6a2288a5c Binary files /dev/null and b/docs/src/assets/showcase-images/cfntracker.webp differ diff --git a/docs/src/assets/showcase-images/clave.png b/docs/src/assets/showcase-images/clave.png new file mode 100644 index 000000000..3ee29c303 Binary files /dev/null and b/docs/src/assets/showcase-images/clave.png differ diff --git a/docs/src/assets/showcase-images/emailit.webp b/docs/src/assets/showcase-images/emailit.webp new file mode 100644 index 000000000..fc1b9a51a Binary files /dev/null and b/docs/src/assets/showcase-images/emailit.webp differ diff --git a/docs/src/assets/showcase-images/encrypteasy.webp b/docs/src/assets/showcase-images/encrypteasy.webp new file mode 100644 index 000000000..c0789a3e3 Binary files /dev/null and b/docs/src/assets/showcase-images/encrypteasy.webp differ diff --git a/docs/src/assets/showcase-images/esp-studio.png b/docs/src/assets/showcase-images/esp-studio.png new file mode 100644 index 000000000..9cb7d39fa Binary files /dev/null and b/docs/src/assets/showcase-images/esp-studio.png differ diff --git a/docs/src/assets/showcase-images/filehound.webp b/docs/src/assets/showcase-images/filehound.webp new file mode 100644 index 000000000..92769ca8e Binary files /dev/null and b/docs/src/assets/showcase-images/filehound.webp differ diff --git a/docs/src/assets/showcase-images/hiposter.webp b/docs/src/assets/showcase-images/hiposter.webp new file mode 100644 index 000000000..7c7510ea1 Binary files /dev/null and b/docs/src/assets/showcase-images/hiposter.webp differ diff --git a/docs/src/assets/showcase-images/mac-app.png b/docs/src/assets/showcase-images/mac-app.png new file mode 100644 index 000000000..1f4c85304 Binary files /dev/null and b/docs/src/assets/showcase-images/mac-app.png differ diff --git a/docs/src/assets/showcase-images/mchat.png b/docs/src/assets/showcase-images/mchat.png new file mode 100644 index 000000000..0942c482b Binary files /dev/null and b/docs/src/assets/showcase-images/mchat.png differ diff --git a/docs/src/assets/showcase-images/minecraft-mod-updater.webp b/docs/src/assets/showcase-images/minecraft-mod-updater.webp new file mode 100644 index 000000000..c8e011cf3 Binary files /dev/null and b/docs/src/assets/showcase-images/minecraft-mod-updater.webp differ diff --git a/docs/src/assets/showcase-images/minesweeper-xp.webp b/docs/src/assets/showcase-images/minesweeper-xp.webp new file mode 100644 index 000000000..b3c5ca26b Binary files /dev/null and b/docs/src/assets/showcase-images/minesweeper-xp.webp differ diff --git a/docs/src/assets/showcase-images/modalfilemanager.webp b/docs/src/assets/showcase-images/modalfilemanager.webp new file mode 100644 index 000000000..2fdf219fc Binary files /dev/null and b/docs/src/assets/showcase-images/modalfilemanager.webp differ diff --git a/docs/src/assets/showcase-images/mollywallet.webp b/docs/src/assets/showcase-images/mollywallet.webp new file mode 100644 index 000000000..11641f8ca Binary files /dev/null and b/docs/src/assets/showcase-images/mollywallet.webp differ diff --git a/docs/src/assets/showcase-images/october.webp b/docs/src/assets/showcase-images/october.webp new file mode 100644 index 000000000..ceec1c573 Binary files /dev/null and b/docs/src/assets/showcase-images/october.webp differ diff --git a/docs/src/assets/showcase-images/optimus.webp b/docs/src/assets/showcase-images/optimus.webp new file mode 100644 index 000000000..0aac84058 Binary files /dev/null and b/docs/src/assets/showcase-images/optimus.webp differ diff --git a/docs/src/assets/showcase-images/portfall.webp b/docs/src/assets/showcase-images/portfall.webp new file mode 100644 index 000000000..12f8d6e5b Binary files /dev/null and b/docs/src/assets/showcase-images/portfall.webp differ diff --git a/docs/src/assets/showcase-images/resizem.webp b/docs/src/assets/showcase-images/resizem.webp new file mode 100644 index 000000000..aaee1c806 Binary files /dev/null and b/docs/src/assets/showcase-images/resizem.webp differ diff --git a/docs/src/assets/showcase-images/restic-browser-2.png b/docs/src/assets/showcase-images/restic-browser-2.png new file mode 100644 index 000000000..b986bfe68 Binary files /dev/null and b/docs/src/assets/showcase-images/restic-browser-2.png differ diff --git a/docs/src/assets/showcase-images/riftshare-main.webp b/docs/src/assets/showcase-images/riftshare-main.webp new file mode 100644 index 000000000..2d6a8fb3a Binary files /dev/null and b/docs/src/assets/showcase-images/riftshare-main.webp differ diff --git a/docs/src/assets/showcase-images/scriptbar.webp b/docs/src/assets/showcase-images/scriptbar.webp new file mode 100644 index 000000000..92463fdb9 Binary files /dev/null and b/docs/src/assets/showcase-images/scriptbar.webp differ diff --git a/docs/src/assets/showcase-images/snippetexpandergui-add-snippet.png b/docs/src/assets/showcase-images/snippetexpandergui-add-snippet.png new file mode 100644 index 000000000..0f34e4f53 Binary files /dev/null and b/docs/src/assets/showcase-images/snippetexpandergui-add-snippet.png differ diff --git a/docs/src/assets/showcase-images/snippetexpandergui-search-and-paste.png b/docs/src/assets/showcase-images/snippetexpandergui-search-and-paste.png new file mode 100644 index 000000000..d87878c29 Binary files /dev/null and b/docs/src/assets/showcase-images/snippetexpandergui-search-and-paste.png differ diff --git a/docs/src/assets/showcase-images/snippetexpandergui-select-snippet.png b/docs/src/assets/showcase-images/snippetexpandergui-select-snippet.png new file mode 100644 index 000000000..b82468c80 Binary files /dev/null and b/docs/src/assets/showcase-images/snippetexpandergui-select-snippet.png differ diff --git a/docs/src/assets/showcase-images/surge.png b/docs/src/assets/showcase-images/surge.png new file mode 100644 index 000000000..d732fabe8 Binary files /dev/null and b/docs/src/assets/showcase-images/surge.png differ diff --git a/docs/src/assets/showcase-images/tiny-rdm1.webp b/docs/src/assets/showcase-images/tiny-rdm1.webp new file mode 100644 index 000000000..11b375580 Binary files /dev/null and b/docs/src/assets/showcase-images/tiny-rdm1.webp differ diff --git a/docs/src/assets/showcase-images/tiny-rdm2.webp b/docs/src/assets/showcase-images/tiny-rdm2.webp new file mode 100644 index 000000000..9de730fb4 Binary files /dev/null and b/docs/src/assets/showcase-images/tiny-rdm2.webp differ diff --git a/docs/src/assets/showcase-images/varly2.webp b/docs/src/assets/showcase-images/varly2.webp new file mode 100644 index 000000000..6dbe0c9bf Binary files /dev/null and b/docs/src/assets/showcase-images/varly2.webp differ diff --git a/docs/src/assets/showcase-images/wailsterm.webp b/docs/src/assets/showcase-images/wailsterm.webp new file mode 100644 index 000000000..6d4251a75 Binary files /dev/null and b/docs/src/assets/showcase-images/wailsterm.webp differ diff --git a/docs/src/assets/showcase-images/wally.webp b/docs/src/assets/showcase-images/wally.webp new file mode 100644 index 000000000..150c98c74 Binary files /dev/null and b/docs/src/assets/showcase-images/wally.webp differ diff --git a/docs/src/assets/showcase-images/warmine1.png b/docs/src/assets/showcase-images/warmine1.png new file mode 100644 index 000000000..38441d99d Binary files /dev/null and b/docs/src/assets/showcase-images/warmine1.png differ diff --git a/docs/src/assets/showcase-images/warmine2.png b/docs/src/assets/showcase-images/warmine2.png new file mode 100644 index 000000000..4713462da Binary files /dev/null and b/docs/src/assets/showcase-images/warmine2.png differ diff --git a/docs/src/assets/showcase-images/wombat.webp b/docs/src/assets/showcase-images/wombat.webp new file mode 100644 index 000000000..97f965834 Binary files /dev/null and b/docs/src/assets/showcase-images/wombat.webp differ diff --git a/docs/src/assets/showcase-images/ytd.webp b/docs/src/assets/showcase-images/ytd.webp new file mode 100644 index 000000000..c7988cee5 Binary files /dev/null and b/docs/src/assets/showcase-images/ytd.webp differ diff --git a/docs/src/assets/wails-logo-dark.svg b/docs/src/assets/wails-logo-dark.svg new file mode 100644 index 000000000..dfc54485c --- /dev/null +++ b/docs/src/assets/wails-logo-dark.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/wails-logo-horizontal-dark.svg b/docs/src/assets/wails-logo-horizontal-dark.svg new file mode 100644 index 000000000..01808dca5 --- /dev/null +++ b/docs/src/assets/wails-logo-horizontal-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/wails-logo-horizontal-light.svg b/docs/src/assets/wails-logo-horizontal-light.svg new file mode 100644 index 000000000..465bc5cf9 --- /dev/null +++ b/docs/src/assets/wails-logo-horizontal-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/wails-logo-light.svg b/docs/src/assets/wails-logo-light.svg new file mode 100644 index 000000000..ab02c827a --- /dev/null +++ b/docs/src/assets/wails-logo-light.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/wails_build.mp4 b/docs/src/assets/wails_build.mp4 new file mode 100644 index 000000000..2d1ee783b Binary files /dev/null and b/docs/src/assets/wails_build.mp4 differ diff --git a/docs/src/assets/wails_dev.mp4 b/docs/src/assets/wails_dev.mp4 new file mode 100644 index 000000000..29c2a45a4 Binary files /dev/null and b/docs/src/assets/wails_dev.mp4 differ diff --git a/docs/src/assets/wails_init.mp4 b/docs/src/assets/wails_init.mp4 new file mode 100644 index 000000000..337bc0f5a Binary files /dev/null and b/docs/src/assets/wails_init.mp4 differ diff --git a/docs/src/components/CardAnimation.astro b/docs/src/components/CardAnimation.astro new file mode 100644 index 000000000..5d3de111b --- /dev/null +++ b/docs/src/components/CardAnimation.astro @@ -0,0 +1,62 @@ +--- +--- + diff --git a/docs/src/components/Mermaid.astro b/docs/src/components/Mermaid.astro new file mode 100644 index 000000000..65b7e3ca9 --- /dev/null +++ b/docs/src/components/Mermaid.astro @@ -0,0 +1,59 @@ +--- +export interface Props { + title?: string; +} + +const { title = "" } = Astro.props; +--- + + + +
+
{title}
+
Loading diagram...
+
+ Source +
+
+
diff --git a/docs/src/content/authors.ts b/docs/src/content/authors.ts new file mode 100644 index 000000000..0500490f3 --- /dev/null +++ b/docs/src/content/authors.ts @@ -0,0 +1,11 @@ +import type { StarlightBlogUserConfig } from "starlight-blog"; + +type Authors = NonNullable["authors"]; +export const authors: Authors = { + leaanthony: { + name: "Lea Anthony", + title: "Maintainer of Wails", + url: "https://github.com/leaanthony", + picture: "https://github.com/leaanthony.png", + }, +}; diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts new file mode 100644 index 000000000..dae3197fe --- /dev/null +++ b/docs/src/content/config.ts @@ -0,0 +1,10 @@ +import { defineCollection } from "astro:content"; +import { docsSchema, i18nSchema } from "@astrojs/starlight/schema"; +import { blogSchema } from "starlight-blog/schema"; + +export const collections = { + i18n: defineCollection({ type: "data", schema: i18nSchema() }), + docs: defineCollection({ + schema: docsSchema({ extend: (context) => blogSchema(context) }), + }), +}; diff --git a/docs/src/content/docs/blog/2021-09-27-v2-beta1-release-notes.md b/docs/src/content/docs/blog/2021-09-27-v2-beta1-release-notes.md new file mode 100644 index 000000000..dbc8d9776 --- /dev/null +++ b/docs/src/content/docs/blog/2021-09-27-v2-beta1-release-notes.md @@ -0,0 +1,206 @@ +--- +slug: blog/wails-v2-beta-for-windows +title: Wails v2 Beta for Windows +authors: [leaanthony] +tags: [wails, v2] +date: 2021-09-27 +--- + +![wails screenshot](../../../assets/blog-images/wails.webp) + +When I first announced Wails on Reddit, just over 2 years ago from a train in +Sydney, I did not expect it to get much attention. A few days later, a prolific +tech vlogger released a tutorial video, gave it a positive review and from that +point on, interest in the project has skyrocketed. + +It was clear that people were excited about adding web frontends to their Go +projects, and almost immediately pushed the project beyond the proof of concept +that I had created. At the time, Wails used the +[webview](https://github.com/webview/webview) project to handle the frontend, +and the only option for Windows was the IE11 renderer. Many bug reports were +rooted in this limitation: poor JavaScript/CSS support and no dev tools to debug +it. This was a frustrating development experience but there wasn't much that +could have been done to rectify it. + +For a long time, I'd firmly believed that Microsoft would eventually have to +sort out their browser situation. The world was moving on, frontend development +was booming and IE wasn't cutting it. When Microsoft announced the move to using +Chromium as the basis for their new browser direction, I knew it was only a +matter of time until Wails could use it, and move the Windows developer +experience to the next level. + +Today, I am pleased to announce: **Wails v2 Beta for Windows**! There's a huge +amount to unpack in this release, so grab a drink, take a seat and we'll +begin... + +### No CGO Dependency! + +No, I'm not joking: _No_ _CGO_ _dependency_ 🤯! The thing about Windows is that, +unlike MacOS and Linux, it doesn't come with a default compiler. In addition, +CGO requires a mingw compiler and there's a ton of different installation +options. Removing the CGO requirement has massively simplified setup, as well as +making debugging an awful lot easier. Whilst I have put a fair bit of effort in +getting this working, the majority of the credit should go to +[John Chadwick](https://github.com/jchv) for not only starting a couple of +projects to make this possible, but also being open to someone taking those +projects and building on them. Credit also to +[Tad Vizbaras](https://github.com/tadvi) whose +[winc](https://github.com/tadvi/winc) project started me down this path. + +### WebView2 Chromium Renderer + +![devtools screenshot](../../../assets/blog-images/devtools.png) + +Finally, Windows developers get a first class rendering engine for their +applications! Gone are the days of contorting your frontend code to work on +Windows. On top of that, you get a first-class developer tools experience! + +The WebView2 component does, however, have a requirement to have the +`WebView2Loader.dll` sitting alongside the binary. This makes distribution just +that little bit more painful than we gophers are used to. All solutions and +libraries (that I know of) that use WebView2 have this dependency. + +However, I'm really excited to announce that Wails applications _have no such +requirement_! Thanks to the wizardry of +[John Chadwick](https://github.com/jchv), we are able to bundle this dll inside +the binary and get Windows to load it as if it were present on disk. + +Gophers rejoice! The single binary dream lives on! + +### New Features + +![wails-menus screenshot](../../../assets/blog-images/wails-menus.webp) + +There were a lot of requests for native menu support. Wails has finally got you +covered. Application menus are now available and include support for most native +menu features. This includes standard menu items, checkboxes, radio groups, +submenus and separators. + +There were a huge number of requests in v1 for the ability to have greater +control of the window itself. I'm happy to announce that there's new runtime +APIs specifically for this. It's feature-rich and supports multi-monitor +configurations. There is also an improved dialogs API: Now, you can have modern, +native dialogs with rich configuration to cater for all your dialog needs. + +There is now the option to generate IDE configuration along with your project. +This means that if you open your project in a supported IDE, it will already be +configured for building and debugging your application. Currently VSCode is +supported but we hope to support other IDEs such as Goland soon. + +![vscode screenshot](../../../assets/blog-images/vscode.webp) + +### No requirement to bundle assets + +A huge pain-point of v1 was the need to condense your entire application down to +single JS & CSS files. I'm happy to announce that for v2, there is no +requirement to bundle assets, in any way, shape or form. Want to load a local +image? Use an `` tag with a local src path. Want to use a cool font? Copy +it in and add the path to it in your CSS. + +> Wow, that sounds like a webserver... + +Yes, it works just like a webserver, except it isn't. + +> So how do I include my assets? + +You just pass a single `embed.FS` that contains all your assets into your +application configuration. They don't even need to be in the top directory - +Wails will just work it out for you. + +### New Development Experience + +![browser screenshot](../../../assets/blog-images/browser.webp) + +Now that assets don't need to be bundled, it's enabled a whole new development +experience. The new `wails dev` command will build and run your application, but +instead of using the assets in the `embed.FS`, it loads them directly from disk. + +It also provides the additional features: + +- Hot reload - Any changes to frontend assets will trigger and auto reload of + the application frontend +- Auto rebuild - Any changes to your Go code will rebuild and relaunch your + application + +In addition to this, a webserver will start on port 34115. This will serve your +application to any browser that connects to it. All connected web browsers will +respond to system events like hot reload on asset change. + +In Go, we are used to dealing with structs in our applications. It's often +useful to send structs to our frontend and use them as state in our application. +In v1, this was a very manual process and a bit of a burden on the developer. +I'm happy to announce that in v2, any application run in dev mode will +automatically generate TypeScript models for all structs that are input or +output parameters to bound methods. This enables seamless interchange of data +models between the two worlds. + +In addition to this, another JS module is dynamically generated wrapping all +your bound methods. This provides JSDoc for your methods, providing code +completion and hinting in your IDE. It's really cool when you get data models +auto-imported when hitting tab in an auto-generated module wrapping your Go +code! + +### Remote Templates + +![remote screenshot](../../../assets/blog-images/remote.webp) + +Getting an application up and running quickly was always a key goal for the +Wails project. When we launched, we tried to cover a lot of the modern +frameworks at the time: react, vue and angular. The world of frontend +development is very opinionated, fast moving and hard to keep on top of! As a +result, we found our base templates getting out of date pretty quickly and this +caused a maintenance headache. It also meant that we didn't have cool modern +templates for the latest and greatest tech stacks. + +With v2, I wanted to empower the community by giving you the ability to create +and host templates yourselves, rather than rely on the Wails project. So now you +can create projects using community supported templates! I hope this will +inspire developers to create a vibrant ecosystem of project templates. I'm +really quite excited about what our developer community can create! + +### In Conclusion + +Wails v2 represents a new foundation for the project. The aim of this release is +to get feedback on the new approach, and to iron out any bugs before a full +release. Your input would be most welcome. Please direct any feedback to the +[v2 Beta](https://github.com/wailsapp/wails/discussions/828) discussion board. + +There were many twists and turns, pivots and u-turns to get to this point. This +was due partly to early technical decisions that needed changing, and partly +because some core problems we had spent time building workarounds for were fixed +upstream: Go’s embed feature is a good example. Fortunately, everything came +together at the right time, and today we have the very best solution that we can +have. I believe the wait has been worth it - this would not have been possible +even 2 months ago. + +I also need to give a huge thank you :pray: to the following people because +without them, this release just wouldn't exist: + +- [Misite Bao](https://github.com/misitebao) - An absolute workhorse on the + Chinese translations and an incredible bug finder. +- [John Chadwick](https://github.com/jchv) - His amazing work on + [go-webview2](https://github.com/jchv/go-webview2) and + [go-winloader](https://github.com/jchv/go-winloader) have made the Windows + version we have today possible. +- [Tad Vizbaras](https://github.com/tadvi) - Experimenting with his + [winc](https://github.com/tadvi/winc) project was the first step down the path + to a pure Go Wails. +- [Mat Ryer](https://github.com/matryer) - His support, encouragement and + feedback has really helped drive the project forward. + +And finally, I'd like to give a special thank you to all the +[project sponsors](/credits#sponsors), including +[JetBrains](https://www.jetbrains.com?from=Wails), whose support drives the +project in many ways behind the scenes. + +I look forward to seeing what people build with Wails in this next exciting +phase of the project! + +Lea. + +PS: MacOS and Linux users need not feel left out - porting to this new +foundation is actively under way and most of the hard work has already been +done. Hang in there! + +PPS: If you or your company find Wails useful, please consider +[sponsoring the project](https://github.com/sponsors/leaanthony). Thanks! diff --git a/docs/src/content/docs/blog/2021-11-08-v2-beta2-release-notes.md b/docs/src/content/docs/blog/2021-11-08-v2-beta2-release-notes.md new file mode 100644 index 000000000..31d53e1bc --- /dev/null +++ b/docs/src/content/docs/blog/2021-11-08-v2-beta2-release-notes.md @@ -0,0 +1,166 @@ +--- +slug: blog/wails-v2-beta-for-mac +title: Wails v2 Beta for MacOS +authors: [leaanthony] +tags: [wails, v2] +date: 2021-11-08 +--- + +![wails-mac screenshot](../../../assets/blog-images/wails-mac.webp) + +Today marks the first beta release of Wails v2 for Mac! It's taken quite a while +to get to this point and I'm hoping that today's release will give you something +that's reasonably useful. There have been a number of twists and turns to get to +this point and I'm hoping, with your help, to iron out the crinkles and get the +Mac port polished for the final v2 release. + +You mean this isn't ready for production? For your use case, it may well be +ready, but there are still a number of known issues so keep your eye on +[this project board](https://github.com/wailsapp/wails/projects/7) and if you +would like to contribute, you'd be very welcome! + +So what's new for Wails v2 for Mac vs v1? Hint: It's pretty similar to the +Windows Beta :wink: + +### New Features + +![wails-menus-mac screenshot](../../../assets/blog-images/wails-menus-mac.webp) + +There were a lot of requests for native menu support. Wails has finally got you +covered. Application menus are now available and include support for most native +menu features. This includes standard menu items, checkboxes, radio groups, +submenus and separators. + +There were a huge number of requests in v1 for the ability to have greater +control of the window itself. I'm happy to announce that there's new runtime +APIs specifically for this. It's feature-rich and supports multi-monitor +configurations. There is also an improved dialogs API: Now, you can have modern, +native dialogs with rich configuration to cater for all your dialog needs. + +### Mac Specific Options + +In addition to the normal application options, Wails v2 for Mac also brings some +Mac extras: + +- Make your window all funky and translucent, like all the pretty swift apps! +- Highly customisable titlebar +- We support the NSAppearance options for the application +- Simple config to auto-create an "About" menu + +### No requirement to bundle assets + +A huge pain-point of v1 was the need to condense your entire application down to +single JS & CSS files. I'm happy to announce that for v2, there is no +requirement to bundle assets, in any way, shape or form. Want to load a local +image? Use an `` tag with a local src path. Want to use a cool font? Copy +it in and add the path to it in your CSS. + +> Wow, that sounds like a webserver... + +Yes, it works just like a webserver, except it isn't. + +> So how do I include my assets? + +You just pass a single `embed.FS` that contains all your assets into your +application configuration. They don't even need to be in the top directory - +Wails will just work it out for you. + +### New Development Experience + +Now that assets don't need to be bundled, it's enabled a whole new development +experience. The new `wails dev` command will build and run your application, but +instead of using the assets in the `embed.FS`, it loads them directly from disk. + +It also provides the additional features: + +- Hot reload - Any changes to frontend assets will trigger and auto reload of + the application frontend +- Auto rebuild - Any changes to your Go code will rebuild and relaunch your + application + +In addition to this, a webserver will start on port 34115. This will serve your +application to any browser that connects to it. All connected web browsers will +respond to system events like hot reload on asset change. + +In Go, we are used to dealing with structs in our applications. It's often +useful to send structs to our frontend and use them as state in our application. +In v1, this was a very manual process and a bit of a burden on the developer. +I'm happy to announce that in v2, any application run in dev mode will +automatically generate TypeScript models for all structs that are input or +output parameters to bound methods. This enables seamless interchange of data +models between the two worlds. + +In addition to this, another JS module is dynamically generated wrapping all +your bound methods. This provides JSDoc for your methods, providing code +completion and hinting in your IDE. It's really cool when you get data models +auto-imported when hitting tab in an auto-generated module wrapping your Go +code! + +### Remote Templates + +![remote-mac screenshot](../../../assets/blog-images/remote-mac.webp) + +Getting an application up and running quickly was always a key goal for the +Wails project. When we launched, we tried to cover a lot of the modern +frameworks at the time: react, vue and angular. The world of frontend +development is very opinionated, fast moving and hard to keep on top of! As a +result, we found our base templates getting out of date pretty quickly and this +caused a maintenance headache. It also meant that we didn't have cool modern +templates for the latest and greatest tech stacks. + +With v2, I wanted to empower the community by giving you the ability to create +and host templates yourselves, rather than rely on the Wails project. So now you +can create projects using community supported templates! I hope this will +inspire developers to create a vibrant ecosystem of project templates. I'm +really quite excited about what our developer community can create! + +### Native M1 Support + +Thanks to the amazing support of [Mat Ryer](https://github.com/matryer/), the +Wails project now supports M1 native builds: + +![build-darwin-arm screenshot](../../../assets/blog-images/build-darwin-arm.webp) + +You can also specify `darwin/amd64` as a target too: + +![build-darwin-amd screenshot](../../../assets/blog-images/build-darwin-amd.webp) + +Oh, I almost forgot.... you can also do `darwin/universal`.... :wink: + +![build-darwin-universal screenshot](../../../assets/blog-images/build-darwin-universal.webp) + +### Cross Compilation to Windows + +Because Wails v2 for Windows is pure Go, you can target Windows builds without +docker. + +![build-cross-windows screenshot](../../../assets/blog-images/build-cross-windows.webp) +bu + +### WKWebView Renderer + +V1 relied on a (now deprecated) WebView component. V2 uses the most recent +WKWebKit component so expect the latest and greatest from Apple. + +### In Conclusion + +As I'd said in the Windows release notes, Wails v2 represents a new foundation +for the project. The aim of this release is to get feedback on the new approach, +and to iron out any bugs before a full release. Your input would be most +welcome! Please direct any feedback to the +[v2 Beta](https://github.com/wailsapp/wails/discussions/828) discussion board. + +And finally, I'd like to give a special thank you to all the +[project sponsors](/credits#sponsors), including +[JetBrains](https://www.jetbrains.com?from=Wails), whose support drive the +project in many ways behind the scenes. + +I look forward to seeing what people build with Wails in this next exciting +phase of the project! + +Lea. + +PS: Linux users, you're next! + +PPS: If you or your company find Wails useful, please consider +[sponsoring the project](https://github.com/sponsors/leaanthony). Thanks! diff --git a/docs/src/content/docs/blog/2022-02-22-v2-beta3-release-notes.md b/docs/src/content/docs/blog/2022-02-22-v2-beta3-release-notes.md new file mode 100644 index 000000000..047236258 --- /dev/null +++ b/docs/src/content/docs/blog/2022-02-22-v2-beta3-release-notes.md @@ -0,0 +1,129 @@ +--- +slug: blog/wails-v2-beta-for-linux +title: Wails v2 Beta for Linux +authors: [leaanthony] +tags: [wails, v2] +date: 2022-02-22 +--- + +![wails-linux screenshot](../../../assets/blog-images/wails-linux.webp) + +I'm pleased to finally announce that Wails v2 is now in beta for Linux! It is +somewhat ironic that the very first experiments with v2 was on Linux and yet it +has ended up as the last release. That being said, the v2 we have today is very +different from those first experiments. So without further ado, let's go over +the new features: + +### New Features + +![wails-menus-linux screenshot](../../../assets/blog-images/wails-menus-linux.webp) + +There were a lot of requests for native menu support. Wails has finally got you +covered. Application menus are now available and include support for most native +menu features. This includes standard menu items, checkboxes, radio groups, +submenus and separators. + +There were a huge number of requests in v1 for the ability to have greater +control of the window itself. I'm happy to announce that there's new runtime +APIs specifically for this. It's feature-rich and supports multi-monitor +configurations. There is also an improved dialogs API: Now, you can have modern, +native dialogs with rich configuration to cater for all your dialog needs. + +### No requirement to bundle assets + +A huge pain-point of v1 was the need to condense your entire application down to +single JS & CSS files. I'm happy to announce that for v2, there is no +requirement to bundle assets, in any way, shape or form. Want to load a local +image? Use an `<../../../assets/blog-images>` tag with a local src path. Want to +use a cool font? Copy it in and add the path to it in your CSS. + +> Wow, that sounds like a webserver... + +Yes, it works just like a webserver, except it isn't. + +> So how do I include my assets? + +You just pass a single `embed.FS` that contains all your assets into your +application configuration. They don't even need to be in the top directory - +Wails will just work it out for you. + +### New Development Experience + +Now that assets don't need to be bundled, it's enabled a whole new development +experience. The new `wails dev` command will build and run your application, but +instead of using the assets in the `embed.FS`, it loads them directly from disk. + +It also provides the additional features: + +- Hot reload - Any changes to frontend assets will trigger an auto reload of the + application frontend +- Auto rebuild - Any changes to your Go code will rebuild and relaunch your + application + +In addition to this, a webserver will start on port 34115. This will serve your +application to any browser that connects to it. All connected web browsers will +respond to system events like hot reload on asset change. + +In Go, we are used to dealing with structs in our applications. It's often +useful to send structs to our frontend and use them as state in our application. +In v1, this was a very manual process and a bit of a burden on the developer. +I'm happy to announce that in v2, any application run in dev mode will +automatically generate TypeScript models for all structs that are input or +output parameters to bound methods. This enables seamless interchange of data +models between the two worlds. + +In addition to this, another JS module is dynamically generated wrapping all +your bound methods. This provides JSDoc for your methods, providing code +completion and hinting in your IDE. It's really cool when you get data models +auto-imported when hitting tab in an auto-generated module wrapping your Go +code! + +### Remote Templates + +![remote-linux screenshot](../../../assets/blog-images/remote-linux.webp) + +Getting an application up and running quickly was always a key goal for the +Wails project. When we launched, we tried to cover a lot of the modern +frameworks at the time: react, vue and angular. The world of frontend +development is very opinionated, fast moving and hard to keep on top of! As a +result, we found our base templates getting out of date pretty quickly and this +caused a maintenance headache. It also meant that we didn't have cool modern +templates for the latest and greatest tech stacks. + +With v2, I wanted to empower the community by giving you the ability to create +and host templates yourselves, rather than rely on the Wails project. So now you +can create projects using community supported templates! I hope this will +inspire developers to create a vibrant ecosystem of project templates. I'm +really quite excited about what our developer community can create! + +### Cross Compilation to Windows + +Because Wails v2 for Windows is pure Go, you can target Windows builds without +docker. + +![build-cross-windows screenshot](../../../assets/blog-images/linux-build-cross-windows.webp) + +### In Conclusion + +As I'd said in the Windows release notes, Wails v2 represents a new foundation +for the project. The aim of this release is to get feedback on the new approach, +and to iron out any bugs before a full release. Your input would be most +welcome! Please direct any feedback to the +[v2 Beta](https://github.com/wailsapp/wails/discussions/828) discussion board. + +Linux is **hard** to support. We expect there to be a number of quirks with the +beta. Please help us to help you by filing detailed bug reports! + +Finally, I'd like to give a special thank you to all the +[project sponsors](/credits#sponsors) whose support drives the project in many +ways behind the scenes. + +I look forward to seeing what people build with Wails in this next exciting +phase of the project! + +Lea. + +PS: The v2 release isn't far off now! + +PPS: If you or your company find Wails useful, please consider +[sponsoring the project](https://github.com/sponsors/leaanthony). Thanks! diff --git a/docs/src/content/docs/blog/2022-09-22-v2-release-notes.md b/docs/src/content/docs/blog/2022-09-22-v2-release-notes.md new file mode 100644 index 000000000..c72d7f566 --- /dev/null +++ b/docs/src/content/docs/blog/2022-09-22-v2-release-notes.md @@ -0,0 +1,198 @@ +--- +slug: blog/wails-v2-released +title: Wails v2 Released +authors: [leaanthony] +tags: [wails, v2] +date: 2022-09-22 +--- + +![montage screenshot](../../../assets/blog-images/montage.png) + +# It's here! + +Today marks the release of [Wails](https://wails.io) v2. It's been about 18 +months since the first v2 alpha and about a year from the first beta release. +I'm truly grateful to everyone involved in the evolution of the project. + +Part of the reason it took that long was due to wanting to get to some +definition of completeness before officially calling it v2. The truth is, +there's never a perfect time to tag a release - there's always outstanding +issues or "just one more" feature to squeeze in. What tagging an imperfect major +release does do, however, is to provide a bit of stability for users of the +project, as well as a bit of a reset for the developers. + +This release is more than I'd ever expected it to be. I hope it gives you as +much pleasure as it has given us to develop it. + +# What _is_ Wails? + +If you are unfamiliar with Wails, it is a project that enables Go programmers to +provide rich frontends for their Go programs using familiar web technologies. +It's a lightweight, Go alternative to Electron. Much more information can be +found on the [official site](https://wails.io/docs/introduction). + +# What's new? + +The v2 release is a huge leap forward for the project, addressing many of the +pain points of v1. If you have not read any of the blog posts on the Beta +releases for [macOS](/blog/wails-v2-beta-for-mac), +[Windows](/blog/wails-v2-beta-for-windows) or +[Linux](/blog/wails-v2-beta-for-linux), then I encourage you to do so as it +covers all the major changes in more detail. In summary: + +- Webview2 component for Windows that supports modern web standards and + debugging capabilities. +- [Dark / Light theme](https://wails.io/docs/reference/options#theme) + + [custom theming](https://wails.io/docs/reference/options#customtheme) on Windows. +- Windows now has no CGO requirements. +- Out-of-the-box support for Svelte, Vue, React, Preact, Lit & Vanilla project + templates. +- [Vite](https://vitejs.dev/) integration providing a hot-reload development + environment for your application. +- Native application + [menus](https://wails.io/docs/guides/application-development#application-menu) and + [dialogs](https://wails.io/docs/reference/runtime/dialog). +- Native window translucency effects for + [Windows](https://wails.io/docs/reference/options#windowistranslucent) and + [macOS](https://wails.io/docs/reference/options#windowistranslucent-1). Support for Mica & + Acrylic backdrops. +- Easily generate an [NSIS installer](https://wails.io/docs/guides/windows-installer) for + Windows deployments. +- A rich [runtime library](https://wails.io/docs/reference/runtime/intro) providing utility + methods for window manipulation, eventing, dialogs, menus and logging. +- Support for [obfuscating](https://wails.io/docs/guides/obfuscated) your application using + [garble](https://github.com/burrowers/garble). +- Support for compressing your application using [UPX](https://upx.github.io/). +- Automatic TypeScript generation of Go structs. More info + [here](https://wails.io/docs/howdoesitwork#calling-bound-go-methods). +- No extra libraries or DLLs are required to be shipped with your application. + For any platform. +- No requirement to bundle frontend assets. Just develop your application like + any other web application. + +# Credit & Thanks + +Getting to v2 has been a huge effort. There have been ~2.2K commits by 89 +contributors between the initial alpha and the release today, and many, many +more that have provided translations, testing, feedback and help on the +discussion forums as well as the issue tracker. I'm so unbelievably grateful to +each one of you. I'd also like to give an extra special thank you to all the +project sponsors who have provided guidance, advice and feedback. Everything you +do is hugely appreciated. + +There are a few people I'd like to give special mention to: + +Firstly, a **huge** thank you to [@stffabi](https://github.com/stffabi) who has +provided so many contributions which we all benefit from, as well as providing a +lot of support on many issues. He has provided some key features such as the +external dev server support which transformed our dev mode offering by allowing +us to hook into [Vite](https://vitejs.dev/)'s superpowers. It's fair to say that +Wails v2 would be a far less exciting release without his +[incredible contributions](https://github.com/wailsapp/wails/commits?author=stffabi&since=2020-01-04). +Thank you so much @stffabi! + +I'd also like to give a huge shout-out to +[@misitebao](https://github.com/misitebao) who has tirelessly been maintaining +the website, as well as providing Chinese translations, managing Crowdin and +helping new translators get up to speed. This is a hugely important task, and +I'm extremely grateful for all the time and effort put into this! You rock! + +Last, but not least, a huge thank you to Mat Ryer who has provided advice and +support during the development of v2. Writing xBar together using an early Alpha +of v2 was helpful in shaping the direction of v2, as well as give me an +understanding of some design flaws in the early releases. I'm happy to announce +that as of today, we will start to port xBar to Wails v2, and it will become the +flagship application for the project. Cheers Mat! + +# Lessons Learnt + +There are a number of lessons learnt in getting to v2 that will shape +development moving forward. + +## Smaller, Quicker, Focused Releases + +In the course of developing v2, there were many features and bug fixes that were +developed on an ad-hoc basis. This led to longer release cycles and were harder +to debug. Moving forward, we are going to create releases more often that will +include a reduced number of features. A release will involve updates to +documentation as well as thorough testing. Hopefully, these smaller, quicker, +focussed releases will lead to fewer regressions and better quality +documentation. + +## Encourage Engagement + +When starting this project, I wanted to immediately help everyone who had a +problem. Issues were "personal" and I wanted them resolved as quickly as +possible. This is unsustainable and ultimately works against the longevity of +the project. Moving forward, I will be giving more space for people to get +involved in answering questions and triaging issues. It would be good to get +some tooling to help with this so if you have any suggestions, please join in +the discussion [here](https://github.com/wailsapp/wails/discussions/1855). + +## Learning to say No + +The more people that engage with an Open Source project, the more requests there +will be for additional features that may or may not be useful to the majority of +people. These features will take an initial amount of time to develop and debug, +and incur an ongoing maintenance cost from that point on. I myself am the most +guilty of this, often wanting to "boil the sea" rather than provide the minimum +viable feature. Moving forward, we will need to say "No" a bit more to adding +core features and focus our energies on a way to empower developers to provide +that functionality themselves. We are looking seriously into plugins for this +scenario. This will allow anyone to extend the project as they see fit, as well +as providing an easy way to contribute towards the project. + +# Looking to the Future + +There are so many core features we are looking at to add to Wails in the next +major development cycle already. The +[roadmap](https://github.com/wailsapp/wails/discussions/1484) is full of +interesting ideas, and I'm keen to start work on them. One of the big asks has +been for multiple window support. It's a tricky one and to do it right, and we +may need to look at providing an alternative API, as the current one was not +designed with this in mind. Based on some preliminary ideas and feedback, I +think you'll like where we're looking to go with it. + +I'm personally very excited at the prospect of getting Wails apps running on +mobile. We already have a demo project showing that it is possible to run a +Wails app on Android, so I'm really keen to explore where we can go with this! + +A final point I'd like to raise is that of feature parity. It has long been a +core principle that we wouldn't add anything to the project without there being +full cross-platform support for it. Whilst this has proven to be (mainly) +achievable so far, it has really held the project back in releasing new +features. Moving forward, we will be adopting a slightly different approach: any +new feature that cannot be immediately released for all platforms will be +released under an experimental configuration or API. This allows early adopters +on certain platforms to try the feature and provide feedback that will feed into +the final design of the feature. This, of course, means that there are no +guarantees of API stability until it is fully supported by all the platforms it +can be supported on, but at least it will unblock development. + +# Final Words + +I'm really proud of what we've been able to achieve with the V2 release. It's +amazing to see what people have already been able to build using the beta +releases so far. Quality applications like [Varly](https://varly.app/), +[Surge](https://getsurge.io/) and [October](https://october.utf9k.net/). I +encourage you to check them out. + +This release was achieved through the hard work of many contributors. Whilst it +is free to download and use, it has not come about through zero cost. Make no +mistakes, this project has come at considerable cost. It has not only been my +time and the time of each and every contributor, but also the cost of absence +from friends and families of each of those people too. That's why I'm extremely +grateful for every second that has been dedicated to making this project happen. +The more contributors we have, the more this effort can be spread out and the +more we can achieve together. I'd like to encourage you all to pick one thing +that you can contribute, whether it is confirming someone's bug, suggesting a +fix, making a documentation change or helping out someone who needs it. All of +these small things have such a huge impact! It would be so awesome if you too +were part of the story in getting to v3. + +Enjoy! + +‐ Lea + +PS: If you or your company find Wails useful, please consider +[sponsoring the project](https://github.com/sponsors/leaanthony). Thanks! diff --git a/docs/src/content/docs/blog/2023-01-17-v3-roadmap.md b/docs/src/content/docs/blog/2023-01-17-v3-roadmap.md new file mode 100644 index 000000000..a7d0d3b94 --- /dev/null +++ b/docs/src/content/docs/blog/2023-01-17-v3-roadmap.md @@ -0,0 +1,256 @@ +--- +slug: blog/the-road-to-wails-v3 +title: The Road to Wails v3 +authors: [leaanthony] +tags: [wails, v3] +date: 2023-01-17 +--- + +![multiwindow screenshot](../../../assets/blog-images/multiwindow.webp) + +# Introduction + +Wails is a project that simplifies the ability to write cross-platform desktop +applications using Go. It uses native webview components for the frontend (not +embedded browsers), bringing the power of the world's most popular UI system to +Go, whilst remaining lightweight. + +Version 2 was released on the 22nd of September 2022 and brought with it a lot +of enhancements including: + +- Live development, leveraging the popular Vite project +- Rich functionality for managing windows and creating menus +- Microsoft's WebView2 component +- Generation of Typescript models that mirror your Go structs +- Creating of NSIS Installer +- Obfuscated builds + +Right now, Wails v2 provides powerful tooling for creating rich, cross-platform +desktop applications. + +This blog post aims to look at where the project is at right now and what we can +improve on moving forward. + +# Where are we now? + +It's been incredible to see the popularity of Wails rising since the v2 release. +I'm constantly amazed by the creativity of the community and the wonderful +things that are being built with it. With more popularity, comes more eyes on +the project. And with that, more feature requests and bug reports. + +Over time, I've been able to identify some of the most pressing issues facing +the project. I've also been able to identify some of the things that are holding +the project back. + +## Current issues + +I've identified the following areas that I feel are holding the project back: + +- The API +- Bindings generation +- The Build System + +### The API + +The API to build a Wails application currently consists of 2 parts: + +- The Application API +- The Runtime API + +The Application API famously has only 1 function: `Run()` which takes a heap of +options which govern how the application will work. Whilst this is very simple +to use, it is also very limiting. It is a "declarative" approach which hides a +lot of the underlying complexity. For instance, there is no handle to the main +window, so you can't interact with it directly. For that, you need to use the +Runtime API. This is a problem when you start to want to do more complex things +like create multiple windows. + +The Runtime API provides a lot of utility functions for the developer. This +includes: + +- Window management +- Dialogs +- Menus +- Events +- Logs + +There are a number of things I am not happy with the Runtime API. The first is +that it requires a "context" to be passed around. This is both frustrating and +confusing for new developers who pass in a context and then get a runtime error. + +The biggest issue with the Runtime API is that it was designed for applications +that only use a single window. Over time, the demand for multiple windows has +grown and the API is not well suited to this. + +### Thoughts on the v3 API + +Wouldn't it be great if we could do something like this? + +```go +func main() { + app := wails.NewApplication(options.App{}) + myWindow := app.NewWindow(options.Window{}) + myWindow.SetTitle("My Window") + myWindow.On(events.Window.Close, func() { + app.Quit() + }) + app.Run() +} +``` + +This programmatic approach is far more intuitive and allows the developer to +interact with the application elements directly. All current runtime methods for +windows would simply be methods on the window object. For the other runtime +methods, we could move them to the application object like so: + +```go +app := wails.NewApplication(options.App{}) +app.NewInfoDialog(options.InfoDialog{}) +app.Log.Info("Hello World") +``` + +This is a much more powerful API which will allow for more complex applications +to be built. It also allows for the creation of multiple windows, +[the most up-voted feature on GitHub](https://github.com/wailsapp/wails/issues/1480): + +```go +func main() { + app := wails.NewApplication(options.App{}) + myWindow := app.NewWindow(options.Window{}) + myWindow.SetTitle("My Window") + myWindow.On(events.Window.Close, func() { + app.Quit() + }) + myWindow2 := app.NewWindow(options.Window{}) + myWindow2.SetTitle("My Window 2") + myWindow2.On(events.Window.Close, func() { + app.Quit() + }) + app.Run() +} +``` + +### Bindings generation + +One of the key features of Wails is generating bindings for your Go methods so +they may be called from Javascript. The current method for doing this is a bit +of a hack. It involves building the application with a special flag and then +running the resultant binary which uses reflection to determine what has been +bound. This leads to a bit of a chicken and egg situation: You can't build the +application without the bindings and you can't generate the bindings without +building the application. There are many ways around this but the best one would +be not to use this approach at all. + +There were a number of attempts at writing a static analyser for Wails projects +but they didn't get very far. In more recent times, it has become slightly +easier to do this with more material available on the subject. + +Compared to reflection, the AST approach is much faster however it is +significantly more complicated. To start with, we may need to impose certain +constraints on how to specify bindings in the code. The goal is to support the +most common use cases and then expand it later on. + +### The Build System + +Like the declarative approach to the API, the build system was created to hide +the complexities of building a desktop application. When you run `wails build`, +it does a lot of things behind the scenes: + +- Builds the backend binary for bindings and generates the bindings +- Installs the frontend dependencies +- Builds the frontend assets +- Determines if the application icon is present and if so, embeds it +- Builds the final binary +- If the build is for `darwin/universal` it builds 2 binaries, one for + `darwin/amd64` and one for `darwin/arm64` and then creates a fat binary using + `lipo` +- If compression is required, it compresses the binary with UPX +- Determines if this binary is to be packaged and if so: + - Ensures the icon and application manifest are compiled into the binary + (Windows) + - Builds out the application bundle, generates the icon bundle and copies it, + the binary and Info.plist to the application bundle (Mac) +- If an NSIS installer is required, it builds it + +This entire process, whilst very powerful, is also very opaque. It is very +difficult to customise it and it is very difficult to debug. + +To address this in v3, I would like to move to a build system that exists +outside of Wails. After using [Task](https://taskfile.dev/) for a while, I am a +big fan of it. It is a great tool for configuring build systems and should be +reasonably familiar to anyone who has used Makefiles. + +The build system would be configured using a `Taskfile.yml` file which would be +generated by default with any of the supported templates. This would have all of +the steps required to do all the current tasks, such as building or packaging +the application, allowing for easy customisation. + +There will be no external requirement for this tooling as it would form part of +the Wails CLI. This means that you can still use `wails build` and it will do +all the things it does today. However, if you want to customise the build +process, you can do so by editing the `Taskfile.yml` file. It also means you can +easily understand the build steps and use your own build system if you wish. + +The missing piece in the build puzzle is the atomic operations in the build +process, such as icon generation, compression and packaging. To require a bunch +of external tooling would not be a great experience for the developer. To +address this, the Wails CLI will provide all these capabilities as part of the +CLI. This means that the builds still work as expected, with no extra external +tooling, however you can replace any step of the build with any tool you like. + +This will be a much more transparent build system which will allow for easier +customisation and address a lot of the issues that have been raised around it. + +## The Payoff + +These positive changes will be a huge benefit to the project: + +- The new API will be much more intuitive and will allow for more complex + applications to be built. +- Using static analysis for bindings generation will be much faster and reduce a + lot of the complexity around the current process. +- Using an established, external build system will make the build process + completely transparent, allowing for powerful customisation. + +Benefits to the project maintainers are: + +- The new API will be much easier to maintain and adapt to new features and + platforms. +- The new build system will be much easier to maintain and extend. I hope this + will lead to a new ecosystem of community driven build pipelines. +- Better separation of concerns within the project. This will make it easier to + add new features and platforms. + +## The Plan + +A lot of the experimentation for this has already been done and it's looking +good. There is no current timeline for this work but I'm hoping by the end of Q1 +2023, there will be an alpha release for Mac to allow the community to test, +experiment with and provide feedback. + +## Summary + +- The v2 API is declarative, hides a lot from the developer and not suitable for + features such as multiple windows. A new API will be created which will be + simpler, intuitive and more powerful. +- The build system is opaque and difficult to customise so we will move to an + external build system which will open it all up. +- The bindings generation is slow and complex so we will move to static analysis + which will remove a lot of the complexity the current method has. + +There has been a lot of work put into the guts of v2 and it's solid. It's now +time to address the layer on top of it and make it a much better experience for +the developer. + +I hope you are as excited about this as I am. I'm looking forward to hearing +your thoughts and feedback. + +Regards, + +‐ Lea + +PS: If you or your company find Wails useful, please consider +[sponsoring the project](https://github.com/sponsors/leaanthony). Thanks! + +PPS: Yes, that's a genuine screenshot of a multi-window application built with +Wails. It's not a mockup. It's real. It's awesome. It's coming soon. diff --git a/docs/src/content/docs/blog/2024-12-03-alpha10-and-new-release-strategy.md b/docs/src/content/docs/blog/2024-12-03-alpha10-and-new-release-strategy.md new file mode 100644 index 000000000..0cba0baa7 --- /dev/null +++ b/docs/src/content/docs/blog/2024-12-03-alpha10-and-new-release-strategy.md @@ -0,0 +1,52 @@ +--- +title: Alpha 10 Released - A New Chapter for Wails +description: Announcing Wails v3 Alpha 10 and our new daily release strategy +date: 2024-12-03 +author: The Wails Team +--- + +# Alpha 10 Released - A New Chapter for Wails + +We're thrilled to announce the release of **Wails v3 Alpha 10** - and it's big! While our release cadence may have seemed slow recently, there's been an incredible amount of work happening behind the scenes. Today marks not just another release, but a shift in how we approach the development of Wails. + +## Why the Wait? + +Like many development teams, we fell into the trap of trying to achieve perfection before each release. We wanted to squash every bug, polish every feature, and ensure everything was just right. But here's the thing - software is never truly bug-free, and waiting for that mythical state only delays getting improvements into your hands. + +## Our New Release Strategy + +Starting today, we're adopting a **daily release strategy**. Any new changes merged during the day will be released at the end of that day. This approach will: + +- Get bug fixes and features to you faster +- Provide more frequent feedback loops +- Speed up our journey to Beta +- Make the development process more transparent + +This means you might see more frequent, smaller releases rather than occasional large ones. We believe this will benefit everyone in the Wails community. + +## How You Can Help + +We've been overwhelmed by the number of people wanting to contribute to Wails! To make it easier for contributors to get involved, we're implementing a new system: + +- Firstly, we are opening up bug reporting for alpha releases. The frequent releases allow us to do this. +- Issues marked with **"Ready for Work"** are open for community contributions +- These issues have clear requirements and are ready to be tackled +- Most importantly, **we need people testing PRs** - this is one of the most valuable contributions you can make + +Testing doesn't require deep knowledge of the codebase, but it provides immense value by ensuring changes work across different environments and use cases. + +## What's Next? + +With our new daily release strategy, expect to see rapid progress toward Beta. We're committed to maintaining momentum and getting Wails v3 to a stable release as quickly as possible without sacrificing quality. + +And who knows? There might be a few surprises along the way... 😉 + +## Thank You + +To everyone who has contributed code, tested releases, reported bugs, or simply used Wails - thank you. Your support and feedback drive this project forward. + +Here's to more frequent releases, faster iteration, and an exciting journey ahead! + +--- + +*Want to get involved? Check out our [GitHub repository](https://github.com/wailsapp/wails) for issues marked "ready for work", or join our community to help test the latest changes.* \ No newline at end of file diff --git a/docs/src/content/docs/changelog.mdx b/docs/src/content/docs/changelog.mdx new file mode 100644 index 000000000..907006685 --- /dev/null +++ b/docs/src/content/docs/changelog.mdx @@ -0,0 +1,762 @@ +--- +title: Changelog +--- + + +Legend: +-  - macOS +- ⊞ - Windows +- 🐧 - Linux + +/*-- +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +- `Added` for new features. +- `Changed` for changes in existing functionality. +- `Deprecated` for soon-to-be removed features. +- `Removed` for now removed features. +- `Fixed` for any bug fixes. +- `Security` in case of vulnerabilities. + +*/ + +/* + ** PLEASE DO NOT UPDATE THIS FILE ** + Updates should be added to `v3/UNRELEASED_CHANGELOG.md` + Thank you! +*/ +## [Unreleased] + +## v3.0.0-alpha.27 - 2025-09-07 + +## Fixed +- Fixed redefinition error for liquid glass demo in [#4542](https://github.com/wailsapp/wails/pull/4542) by @Etesam913 + +## v3.0.0-alpha.26 - 2025-08-24 + +## Added +- Add native Liquid Glass effect support for macOS with NSGlassEffectView (macOS 15.0+) and NSVisualEffectView fallback, including comprehensive material customization options by @leaanthony in [#4534](https://github.com/wailsapp/wails/pull/4534) + +## v3.0.0-alpha.25 - 2025-08-16 + +## Changed +- When running `wails3 update build-assets` with the `-config` parameter, values set via the `-product*` parameters are + no longer ignored, and override the config value. + +## v3.0.0-alpha.24 - 2025-08-13 + +## Added +- Browser URL Sanitisation by @leaanthony in [#4500](https://github.dev/wailsapp/wails/pull/4500). Based on [#4484](https://github.com/wailsapp/wails/pull/4484) by @APShenkin. + +## v3.0.0-alpha.23 - 2025-08-11 + +## Fixed +- Fix SetBackgroundColour on Windows by @PPTGamer in [PR](https://github.com/wailsapp/wails/pull/4492) + +## v3.0.0-alpha.22 - 2025-08-10 + +## Added +- Add Content Protection on Windows/Mac by [@leaanthony](https://github.com/leaanthony) based on the original work of [@Taiterbase](https://github.com/Taiterbase) in this [PR](https://github.com/wailsapp/wails/pull/4241) +- Add support for passing CLI variables to Task commands through `wails3 build` and `wails3 package` aliases (#4422) by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/4488) + +## Changed +- `window.NativeWindowHandle()` -> `window.NativeWindow()` by @leaanthony in [#4471](https://github.com/wailsapp/wails/pull/4471) +- Refactor internal window handling by @leaanthony in [#4471](https://github.com/wailsapp/wails/pull/4471) ++ Fix extra-broad Linux package dependencies, fix outdated RPM dependencies. + +## v3.0.0-alpha.21 - 2025-08-07 + +## Fixed +- Update docs to reflect changes from Manager API Refactoring by @yulesxoxo in [PR #4476](https://github.com/wailsapp/wails/pull/4476) +- Fix Linux .desktop file appicon variable in Linux taskfile [PR #4477](https://github.com/wailsapp/wails/pull/4477) + +## v3.0.0-alpha.20 - 2025-08-06 + +## Fixed +- Update docs to reflect changes from Manager API Refactoring by @yulesxoxo in [PR #4476](https://github.com/wailsapp/wails/pull/4476) + +## v3.0.0-alpha.19 - 2025-08-05 + +## Added +- Support for dropzones with event sourcing dropped element data [@atterpac](https://github.com/atterpac) in [#4318](https://github.com/wailsapp/wails/pull/4318) +- Added `AdditionalLaunchArgs` to `WindowsWindow` options to allow for additional command line arguments to be passed to the WebView2 browser. in [PR](https://github.com/wailsapp/wails/pull/4467) +- Added Run go mod tidy automatically after wails init [@triadmoko](https://github.com/triadmoko) in [PR](https://github.com/wailsapp/wails/pull/4286) +- Windows Snapassist feature by @leaanthony in [PR](https://github.dev/wailsapp/wails/pull/4463) + +## Fixed +- Fix Windows nil pointer dereference bug reported in [#4456](https://github.com/wailsapp/wails/issues/4456) by @leaanthony in [#4460](https://github.com/wailsapp/wails/pull/4460) +- Add support for `allowsBackForwardNavigationGestures` in macOS WKWebView to enable two-finger swipe navigation gestures (#1857) +- Fixes issue where onClick didn't work for menu items initially set as disabled by @leaanthony in [PR #4469](https://github.com/wailsapp/wails/pull/4469). Thanks to @IanVS for the initial investigation. +- Fix Vite server not being cleaned up when build fails (#4403) +- Fixed panic when closing or cancelling a `SaveFileDialog` on windows. Fixed in [PR](https://github.com/wailsapp/wails/pull/4284) by @hkhere +- Fixed HTML level drag and drop on Windows by [@mbaklor](https://github.com/mbaklor) in [#4259](https://github.com/wailsapp/wails/pull/4259) + +## v3.0.0-alpha.18 - 2025-08-03 + +## Added +- Added `AdditionalLaunchArgs` to `WindowsWindow` options to allow for additional command line arguments to be passed to the WebView2 browser. in [PR](https://github.com/wailsapp/wails/pull/4467) +- Added Run go mod tidy automatically after wails init [@triadmoko](https://github.com/triadmoko) in [PR](https://github.com/wailsapp/wails/pull/4286) +- Windows Snapassist feature by @leaanthony in [PR](https://github.dev/wailsapp/wails/pull/4463) + +## Fixed +- Add support for `allowsBackForwardNavigationGestures` in macOS WKWebView to enable two-finger swipe navigation gestures (#1857) +- Fixes issue where onClick didn't work for menu items initially set as disabled by @leaanthony in [PR #4469](https://github.com/wailsapp/wails/pull/4469). Thanks to @IanVS for the initial investigation. +- Fix Vite server not being cleaned up when build fails (#4403) + +## v3.0.0-alpha.17 - 2025-07-31 + +## Fixed +- Fixed notification parsing on Windows @popaprozac in [PR](https://github.com/wailsapp/wails/pull/4450) + +## v3.0.0-alpha.16 - 2025-07-25 + +## Added +- Add Windows `getAccentColor` implementation by [@almas-x](https://github.com/almas-x) in [PR](https://github.com/wailsapp/wails/pull/4427) + +## v3.0.0-alpha.15 - 2025-07-25 + +## Added +- Add Windows `getAccentColor` implementation by [@almas-x](https://github.com/almas-x) in [PR](https://github.com/wailsapp/wails/pull/4427) + +## v3.0.0-alpha.14 - 2025-07-25 + +## Added +- Windows dark theme menus + menubar. By @leaanthony in [a29b4f0861b1d0a700e9eb213c6f1076ec40efd5](https://github.com/wailsapp/wails/commit/a29b4f0861b1d0a700e9eb213c6f1076ec40efd5) +- Rename built-in services for clearer JS/TS bindings by @popaprozac in [PR](https://github.com/wailsapp/wails/pull/4405) + +## v3.0.0-alpha.12 - 2025-07-15 + +### Added +- `app.Env.GetAccentColor` to get the accent color of a user's system. Works on MacOS. by [@etesam913](https://github.com/etesam913) +- Add `window.ToggleFrameless()` api by [@atterpac](https://github.com/atterpac) in [#4137](https://github.com/wailsapp/wails/pull/4137) + +### Fixed +- Fixed doctor command to check for Windows SDK dependencies by [@kodumulo](https://github.com/kodumulo) in [#4390](https://github.com/wailsapp/wails/issues/4390) + + +## v3.0.0-alpha.11 - 2025-07-12 + +## Added +- Add distribution-specific build dependencies for Linux by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/4345) +- Added bindings guide by @atterpac in [PR](https://github.com/wailsapp/wails/pull/4404) + +## v3.0.0-alpha.10 - 2025-07-06 + +### Breaking Changes +- **Manager API Refactoring**: Reorganized application API from flat structure to organized managers for better code organization and discoverability by [@leaanthony](https://github.com/leaanthony) in [#4359](https://github.com/wailsapp/wails/pull/4359) + - `app.NewWebviewWindow()` → `app.Window.New()` + - `app.CurrentWindow()` → `app.Window.Current()` + - `app.GetAllWindows()` → `app.Window.GetAll()` + - `app.WindowByName()` → `app.Window.GetByName()` + - `app.EmitEvent()` → `app.Event.Emit()` + - `app.OnApplicationEvent()` → `app.Event.OnApplicationEvent()` + - `app.OnWindowEvent()` → `app.Event.OnWindowEvent()` + - `app.SetApplicationMenu()` → `app.Menu.SetApplicationMenu()` + - `app.OpenFileDialog()` → `app.Dialog.OpenFile()` + - `app.SaveFileDialog()` → `app.Dialog.SaveFile()` + - `app.MessageDialog()` → `app.Dialog.Message()` + - `app.InfoDialog()` → `app.Dialog.Info()` + - `app.WarningDialog()` → `app.Dialog.Warning()` + - `app.ErrorDialog()` → `app.Dialog.Error()` + - `app.QuestionDialog()` → `app.Dialog.Question()` + - `app.NewSystemTray()` → `app.SystemTray.New()` + - `app.GetSystemTray()` → `app.SystemTray.Get()` + - `app.ShowContextMenu()` → `app.ContextMenu.Show()` + - `app.RegisterKeybinding()` → `app.KeyBinding.Register()` + - `app.UnregisterKeybinding()` → `app.KeyBinding.Unregister()` + - `app.GetPrimaryScreen()` → `app.Screen.GetPrimary()` + - `app.GetAllScreens()` → `app.Screen.GetAll()` + - `app.BrowserOpenURL()` → `app.Browser.OpenURL()` + - `app.Environment()` → `app.Env.GetAll()` + - `app.ClipboardGetText()` → `app.Clipboard.Text()` + - `app.ClipboardSetText()` → `app.Clipboard.SetText()` +- Renamed Service methods: `Name` -> `ServiceName`, `OnStartup` -> `ServiceStartup`, `OnShutdown` -> `ServiceShutdown` by [@leaanthony](https://github.com/leaanthony) +- Moved `Path` and `Paths` methods to `application` package by [@leaanthony](https://github.com/leaanthony) +- The application menu is now macOS only by [@leaanthony](https://github.com/leaanthony) + +### Added + +- **Organized Testing Infrastructure**: Moved Docker test files to dedicated `test/docker/` directory with optimized images and enhanced build reliability by [@leaanthony](https://github.com/leaanthony) in [#4359](https://github.com/wailsapp/wails/pull/4359) +- **Improved Resource Management Patterns**: Added proper event handler cleanup and context-aware goroutine management in examples by [@leaanthony](https://github.com/leaanthony) in [#4359](https://github.com/wailsapp/wails/pull/4359) +- Support aarch64 AppImage builds by [@AkshayKalose](https://github.com/AkshayKalose) in [#3981](https://github.com/wailsapp/wails/pull/3981) +- Add diagnostics section to `wails doctor` by [@leaanthony](https://github.com/leaanthony) +- Add window to context when calling a service method by [@leaanthony](https://github.com/leaanthony) +- Add `window-call` example to demonstrate how to know which window is calling a service by [@leaanthony](https://github.com/leaanthony) +- New Menu guide by [@leaanthony](https://github.com/leaanthony) +- Better panic handling by [@leaanthony](https://github.com/leaanthony) +- New Menu guide by [@leaanthony](https://github.com/leaanthony) +- Add doc comments for Service API by [@fbbdev](https://github.com/fbbdev) in [#4024](https://github.com/wailsapp/wails/pull/4024) +- Add function `application.NewServiceWithOptions` to initialise services with additional configuration by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024) +- Improved menu control by [@FalcoG](https://github.com/FalcoG) and [@leaanthony](https://github.com/leaanthony) in [#4031](https://github.com/wailsapp/wails/pull/4031) +- More documentation by [@leaanthony](https://github.com/leaanthony) +- Support cancellation of events in standard event listeners by [@leaanthony](https://github.com/leaanthony) +- Systray `Hide`, `Show` and `Destroy` support by [@leaanthony](https://github.com/leaanthony) +- Systray `SetTooltip` support by [@leaanthony](https://github.com/leaanthony). Original idea by [@lujihong](https://github.com/wailsapp/wails/issues/3487#issuecomment-2633242304) +- Report package path in binding generator warnings about unsupported types by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Add binding generator support for generic aliases by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Add binding generator support for `omitzero` JSON flag by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Add `//wails:ignore` directive to prevent binding generation for chosen service methods by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Add `//wails:internal` directive on services and models to allow for types that are exported in Go but not in JS/TS by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Add binding generator support for constants of alias type to allow for weakly typed enums by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Add binding generator tests for Go 1.24 features by [@fbbdev](https://github.com/fbbdev) in [#4068](https://github.com/wailsapp/wails/pull/4068) +- Add support for macOS 15 "Sequoia" to `OSInfo.Branding` for improved OS version detection in [#4065](https://github.com/wailsapp/wails/pull/4065) +- Add `PostShutdown` hook for running custom code after the shutdown process completes by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Add `FatalError` struct to support detection of fatal errors in custom error handlers by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Standardise and document service startup and shutdown order by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Add test harness for application startup/shutdown sequence and service startup/shutdown tests by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Add `RegisterService` method for registering services after the application has been created by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Add `MarshalError` field in application and service options for custom error handling in binding calls by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Add cancellable promise wrapper that propagates cancellation requests through promise chains by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- Add the ability to tie binding call cancellation to an `AbortSignal` by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- Support `data-wml-*` attributes for WML alongside the usual `wml-*` attributes by [@leaanthony](https://github.com/leaanthony) +- Add `Configure` method on all services for late configuration/dynamic reconfiguration by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- `fileserver` service sends a 503 Service Unavailable response when unconfigured by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- `kvstore` service provides an in-memory key-value store by default when unconfigured by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add `Load` method on `kvstore` service to reload data from file after config changes by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add `Clear` method on `kvstore` service to delete all keys by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add type `Level` in `log` service to provide JS-side log-level constants by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add `Log` method on `log` service to specify log-level dynamically by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- `sqlite` service provides an in-memory DB by default when unconfigured by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add method `Close` on `sqlite` service to close the DB manually by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add cancellation support for query methods on `sqlite` service by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Add prepared statement support to `sqlite` service with JS bindings by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Gin support by [Lea Anthony](https://github.com/leaanthony) in [PR](https://github.com/wailsapp/wails/pull/3537) based on the original work of [@AnalogJ](https://github.com/AnalogJ) in this [PR](https://github.com/wailsapp/wails/pull/3537) +- Fix auto save and password auto save always enabled by [@oSethoum](https://github.com/osethoum) in [#4134](https://github.com/wailsapp/wails/pull/4134) +- Add `SetMenu()` on window to allow for setting a menu on a window by [@leaanthony](https://github.com/leaanthony) +- Add Notification support by [@popaprozac](https://github.com/popaprozac) in [#4098](https://github.com/wailsapp/wails/pull/4098) +-  Add File Association support for mac by [@wimaha](https://github.com/wimaha) in [#4177](https://github.com/wailsapp/wails/pull/4177) +- Add `wails3 tool version` for semantic version bumping by [@leaanthony](https://github.com/leaanthony) +- Add badging support for macOS and Windows by [@popaprozac](https://github.com/popaprozac) in [#](https://github.com/wailsapp/wails/pull/4234) + +### Fixed + +- Fixed nil pointer dereference in processURLRequest for Mac by [@etesam913](https://github.com/etesam913) in [#4366](https://github.com/wailsapp/wails/pull/4366) +- Fixed a linux bug preventing filtered dialogs by [@bh90210](https://github.com/bh90210) in [#4287](https://github.com/wailsapp/wails/pull/4287) +- Fixed Windows+Linux Edit Menu issues by [@leaanthony](https://github.com/leaanthony) in [#3f78a3a](https://github.com/wailsapp/wails/commit/3f78a3a8ce7837e8b32242c8edbbed431c68c062) +- Updated the minimum system version in macOS .plist files from 10.13.0 to 10.15.0 by [@AkshayKalose](https://github.com/AkshayKalose) in [#3981](https://github.com/wailsapp/wails/pull/3981) +- Window ID skip issue by [@leaanthony](https://github.com/leaanthony) +- Fix nil menu issue when calling RegisterContextMenu by [@leaanthony](https://github.com/leaanthony) +- Fixed dependency cycles in binding generator output by [@fbbdev](https://github.com/fbbdev) in [#4001](https://github.com/wailsapp/wails/pull/4001) +- Fixed use-before-define errors in binding generator output by [@fbbdev](https://github.com/fbbdev) in [#4001](https://github.com/wailsapp/wails/pull/4001) +- Pass build flags to binding generator by [@fbbdev](https://github.com/fbbdev) in [#4023](https://github.com/wailsapp/wails/pull/4023) +- Change paths in windows Taskfile to forward slashes to ensure it works on non-Windows platforms by [@leaanthony](https://github.com/leaanthony) +- Mac + Mac JS events now fixed by [@leaanthony](https://github.com/leaanthony) +- Fixed event deadlock for macOS by [@leaanthony](https://github.com/leaanthony) +- Fixed a `Parameter incorrect` error in Window initialisation on Windows when HTML provided but no JS by [@leaanthony](https://github.com/leaanthony) +- Fixed size of response prefix used for content type sniffing in asset server by [@fbbdev](https://github.com/fbbdev) in [#4049](https://github.com/wailsapp/wails/pull/4049) +- Fixed handling of non-404 responses on root index path in asset server by [@fbbdev](https://github.com/fbbdev) in [#4049](https://github.com/wailsapp/wails/pull/4049) +- Fixed undefined behaviour in binding generator when testing properties of generic types by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed binding generator output for models when underlying type has not the same properties as named wrapper by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed binding generator output for map key types and preprocessing by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed binding generator output for structs that implement marshaler interfaces by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed detection of type cycles involving generic types in binding generator by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed invalid references to unexported models in binding generator output by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Moved injected code to the end of service files by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed handling of errors from file close operations in binding generator by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Suppressed warnings for services that define lifecycle or http methods but no other bound methods by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Fixed non-React templates failing to display Hello World footer when using light system colour scheme by [@marcus-crane](https://github.com/marcus-crane) in [#4056](https://github.com/wailsapp/wails/pull/4056) +- Fixed hidden menu items on macOS by [@leaanthony](https://github.com/leaanthony) +- Fixed handling and formatting of errors in message processors by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +-  Fixed skipped service shutdown when quitting application by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +-  Ensure menu updates occur on the main thread by [@leaanthony](https://github.com/leaanthony) +- The dragging and resizing mechanism is now more robust and matches expected platform behaviour more closely by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- Fixed [#4097](https://github.com/wailsapp/wails/issues/4097) Webpack/angular discards runtime init code by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- Fixed initially-hidden menu items by [@IanVS](https://github.com/IanVS) in [#4116](https://github.com/wailsapp/wails/pull/4116) +- Fixed assetFileServer not serving `.html` files when non-extension request when `[request]` doesn't exist but `[request].html` does +- Fixed icon generation paths by [@robin-samuel](https://github.com/robin-samuel) in [#4125](https://github.com/wailsapp/wails/pull/4125) +- Fixed `fullscreen`, `unfullscreen`, `unminimise` and `unmaximise` events not being emitted by [@oSethoum](https://github.com/osethoum) in [#4130](https://github.com/wailsapp/wails/pull/4130) +- Fixed NSIS Error because of incorrect prefix on default version in config by [@robin-samuel](https://github.com/robin-samuel) in [#4126](https://github.com/wailsapp/wails/pull/4126) +- Fixed Dialogs runtime function returning escaped paths on Windows by [TheGB0077](https://github.com/TheGB0077) in [#4188](https://github.com/wailsapp/wails/pull/4188) +- Fixed Webview2 detection path in HKCU by [@leaanthony](https://github.com/leaanthony). +- Fixed input issue with macOS by [@leaanthony](https://github.com/leaanthony). +- Fixed Windows icon generation task file name by [@yulesxoxo](https://github.com/yulesxoxo) in [#4219](https://github.com/wailsapp/wails/pull/4219). +- Fixed transparency issue for frameless windows by [@leaanthony](https://github.com/leaanthony) based on work by @kron. +- Fixed focus calls when window is disabled or minimised by [@leaanthony](https://github.com/leaanthony) based on work by @kron. +- Fixed system trays not showing after taskbar restarts by [@leaanthony](https://github.com/leaanthony) based on work by @kron. +- Fixed fallbackResponseWriter not implementing Flush() in [#4245](https://github.com/wailsapp/wails/pull/4245) +- Fixed fallbackResponseWriter not implementing Flush() by [@superDingda] in [#4236](https://github.com/wailsapp/wails/issues/4236) +- Fixed macOS window close with pending async Go-bound function call crashes by [@joshhardy](https://github.com/joshhardy) in [#4354](https://github.com/wailsapp/wails/pull/4354) +- Fixed Windows Efficiency mode startup race condition by [@leaanthony](https://github.com/leaanthony) +- Fixed Windows icon handle cleanup by [@leaanthony](https://github.com/leaanthony). +- Fixed `OpenFileManager` on Windows by [@PPTGamer](https://github.com/PPTGamer) in [#4375](https://github.com/wailsapp/wails/pull/4375). + +### Changed + +- Removed `application.WindowIDKey` and `application.WindowNameKey` (replaced by `application.WindowKey`) by [@leaanthony](https://github.com/leaanthony) +- ContextMenuData now returns a string instead of any by [@leaanthony](https://github.com/leaanthony) +- In JS/TS bindings, class fields of fixed-length array types are now initialized with their expected length instead of being empty by [@fbbdev](https://github.com/fbbdev) in [#4001](https://github.com/wailsapp/wails/pull/4001) +- ContextMenuData now returns a string instead of any by [@leaanthony](https://github.com/leaanthony) +- `application.NewService` does not accept options as an optional parameter anymore (use `application.NewServiceWithOptions` instead) by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024) +- Removed `nanoid` dependency by [@leaanthony](https://github.com/leaanthony) +- Updated Window example for mica/acrylic/tabbed window styles by [@leaanthony](https://github.com/leaanthony) +- In JS/TS bindings, `internal.js/ts` model files have been removed; all models can now be found in `models.js/ts` by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- In JS/TS bindings, named types are never rendered as aliases for other named types; the old behaviour is now restricted to aliases by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- In JS/TS bindings, in class mode, struct fields whose type is a type parameter are marked optional and never initialised automatically by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045) +- Remove ESLint from templates by by [@IanVS](https://github.com/IanVS) in [#4059](https://github.com/wailsapp/wails/pull/4059) +- Update copyright date to 2025 by [@IanVS](https://github.com/IanVS) in [#4037](https://github.com/wailsapp/wails/pull/4037) +- Add docs for event.Sender by [@IanVS](https://github.com/IanVS) in [#4075](https://github.com/wailsapp/wails/pull/4075) +- Go 1.24 support by [@leaanthony](https://github.com/leaanthony) +- `ServiceStartup` hooks are now invoked when `App.Run` is called, not in `application.New` by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- `ServiceStartup` errors are now returned from `App.Run` instead of terminating the process by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Binding and dialog calls from JS now reject with error objects instead of strings by [@fbbdev](https://github.com/fbbdev) in [#4066](https://github.com/wailsapp/wails/pull/4066) +- Improved systray menu positioning on Windows by [@leaanthony](https://github.com/leaanthony) +- The JS runtime has been ported to TypeScript by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- The runtime initialises as soon as it is imported, no need to wait for the window to load by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- The runtime does not export an init method anymore. A side effects import can be used to initialise it by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- Bound methods now return a `CancellablePromise` that rejects with a `CancelError` if cancelled. The actual result of the call is discarded by [@fbbdev](https://github.com/fbbdev) in [#4100](https://github.com/wailsapp/wails/pull/4100) +- Built-in service types are now consistently called `Service` by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Built-in service creation functions with options are now consistently called `NewWithConfig` by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- `Select` method on `sqlite` service is now named `Query` for consistency with Go APIs by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067) +- Templates: moved runtime to "dependencies", organized package.json files by [@IanVS](https://github.com/IanVS) in [#4133](https://github.com/wailsapp/wails/pull/4133) +- Creates and ad-hoc signs app bundles in dev to enable certain macOS APIs by [@popaprozac](https://github.com/popaprozac) in [#4171](https://github.com/wailsapp/wails/pull/4171) + +## v3.0.0-alpha.9 - 2025-01-13 + +### Added + +- `app.OpenFileManager(path string, selectFile bool)` to open the system file manager to the path `path` with optional highlighting via `selectFile` by [@Krzysztofz01](https://github.com/Krzysztofz01) [@rcalixte](https://github.com/rcalixte) +- New `-git` flag for `wails3 init` command by [@leaanthony](https://github.com/leaanthony) +- New `wails3 generate webview2bootstrapper` command by [@leaanthony](https://github.com/leaanthony) +- Added `init()` method in runtime to allow manual initialisation of the runtime by [@leaanthony](https://github.com/leaanthony) +- Added `WindowDidMoveDebounceMS` option to Window's WindowOptions by [@leaanthony](https://github.com/leaanthony) +- Added Single Instance feature by [@leaanthony](https://github.com/leaanthony). Based on the [v2 PR](https://github.com/wailsapp/wails/pull/2951) by @APshenkin. +- `wails3 generate template` command by [@leaanthony](https://github.com/leaanthony) +- `wails3 releasenotes` command by [@leaanthony](https://github.com/leaanthony) +- `wails3 update cli` command by [@leaanthony](https://github.com/leaanthony) +- `-clean` option for `wails3 generate bindings` command by [@leaanthony](https://github.com/leaanthony) +- Allow for aarch64 (arm64) AppImage Linux builds by [@AkshayKalose](https://github.com/AkshayKalose) in [#3981](https://github.com/wailsapp/wails/pull/3981) + +### Fixed + +- Fixed min/max width options for linux by @atterpac in [#3979](https://github.com/wailsapp/wails/pull/3979) +- Typescript templates types definitions via npm version bump by @atterpac in [#3966](https://github.com/wailsapp/wails/pull/3966) +- Fix Sveltekit template CSS referance by @atterpac in [#3945](https://github.com/wailsapp/wails/pull/3945) +- Ensure key callbacks in window run() are called on the main thread by [@leaanthony](https://github.com/leaanthony) +- Fix dialog directory chooser examples by [@leaanthony](https://github.com/leaanthony) +- Created new Chinese error page when index.html is missing by [@leaanthony](https://github.com/leaanthony) +-  Ensure `windowDidBecomeKey` callback is running on main thread by [@leaanthony](https://github.com/leaanthony) +-  Support fullscreen for frameless windows by [@leaanthony](https://github.com/leaanthony) +-  Improved window destroying logic by [@leaanthony](https://github.com/leaanthony) +-  Fix window position logic when attached to system trays by [@leaanthony](https://github.com/leaanthony) +-  Support fullscreen for frameless windows by [@leaanthony](https://github.com/leaanthony) +- Fix event handling by [@leaanthony](https://github.com/leaanthony) +- Fixed window shutdown logic by [@leaanthony](https://github.com/leaanthony) +- Common taskfile now defaults to generating Typescript bindings for Typescript templates by [@leaanthony](https://github.com/leaanthony) +- Fix Close application on WM_CLOSE message when no windows are open/systray only by [@mmalcek](https://github.com/mmalcek) in [#3990](https://github.com/wailsapp/wails/pull/3990) +- Fixed garble build by @5aaee9 in [#3192](https://github.com/wailsapp/wails/pull/3192) +- Fixed windows nsis builds by [@leaanthony](https://github.com/leaanthony) + +### Changed + +- Moved build assets to platform specific directories by [@leaanthony](https://github.com/leaanthony) +- Moved and renamed Taskfiles to platform specific directories by [@leaanthony](https://github.com/leaanthony) +- Created a much better experience when `index.html` is missing by [@leaanthony](https://github.com/leaanthony) +- [Windows] Improved performance of minimise and restore by [@leaanthony](https://github.com/leaanthony). Based on original [PR](https://github.com/wailsapp/wails/pull/3955) by [562589540](https://github.com/562589540) +- Removed `ShouldClose` option (Register a hook for events.Common.WindowClosing instead) by [@leaanthony](https://github.com/leaanthony) +- [Windows] Reduced flicker when opening a window by [@leaanthony](https://github.com/leaanthony) +- Removed `Window.Destroy` as this was intended to be an internal function by [@leaanthony](https://github.com/leaanthony) +- Renamed `WindowClose` events to `WindowClosing` by [@leaanthony](https://github.com/leaanthony) +- Frontend builds now use vite environment "development" or "production" depending on build type by [@leaanthony](https://github.com/leaanthony) +- Update to go-webview2 v1.19 by [@leaanthony](https://github.com/leaanthony) + +## v3.0.0-alpha.8.3 - 2024-12-07 + +### Changed + +- Ensure fork of taskfile is used by @leaanthony + +## v3.0.0-alpha.8.2 - 2024-12-07 + +### Changed + +- Update fork of Taskfile to fix version issues when installing using + `go install` by @leaanthony + +## v3.0.0-alpha.8.1 - 2024-12-07 + +### Changed + +- Using fork of Taskfile to fix version issues when installing using + `go install` by @leaanthony + +## v3.0.0-alpha.8 - 2024-12-06 + +### Added + +- Added hyperlink for sponsor by @ansxuman in [#3958](https://github.com/wailsapp/wails/pull/3958) +- Support of linux packaging of deb,rpm, and arch linux packager builds by + @atterpac in [#3909](https://github.com/wailsapp/wails/3909) +- Added Support for darwin universal builds and packages by + [ansxuman](https://github.com/ansxuman) in + [#3902](https://github.com/wailsapp/wails/pull/3902) +- Events documentation to the website by + [atterpac](https://github.com/atterpac) in + [#3867](https://github.com/wailsapp/wails/pull/3867) +- Templates for sveltekit and sveltekit-ts that are set for non-SSR development + by [atterpac](https://github.com/atterpac) in + [#3829](https://github.com/wailsapp/wails/pull/3829) +- Update build assets using new `wails3 update build-assets` command by + [leaanthony](https://github.com/leaanthony) +- Example to test the HTML Drag and Drop API by + [FerroO2000](https://github.com/FerroO2000) in + [#3856](https://github.com/wailsapp/wails/pull/3856) +- File Association support by [leaanthony](https://github.com/leaanthony) in + [#3873](https://github.com/wailsapp/wails/pull/3873) +- New `wails3 generate runtime` command by + [leaanthony](https://github.com/leaanthony) +- New `InitialPosition` option to specify if the window should be centered or + positioned at the given X/Y location by + [leaanthony](https://github.com/leaanthony) in + [#3885](https://github.com/wailsapp/wails/pull/3885) +- Add `Path` & `Paths` methods to `application` package by + [ansxuman](https://github.com/ansxuman) and + [leaanthony](https://github.com/leaanthony) in + [#3823](https://github.com/wailsapp/wails/pull/3823) +- Added `GeneralAutofillEnabled` and `PasswordAutosaveEnabled` Windows options + by [leaanthony](https://github.com/leaanthony) in + [#3766](https://github.com/wailsapp/wails/pull/3766) +- Added the ability to retrieve the window calling a service method by + [leaanthony](https://github.com/leaanthony) in + [#3888](https://github.com/wailsapp/wails/pull/3888) +- Added `EnabledFeatures` and `DisabledFeatures` options for Webview2 by + [leaanthony](https://github.com/leaanthony). +- + +### Changed + +- `service.OnStartup` now shutdowns the application on error and runs + `service.OnShutdown`for any prior services that started by @atterpac in + [#3920](https://github.com/wailsapp/wails/pull/3920) +- Refactored systray click messaging to better align with user interactions by + @atterpac in [#3907](https://github.com/wailsapp/wails/pull/3907) +- Asset embed to include `all:frontend/dist` to support frameworks that generate + subfolders by @atterpac in + [#3887](https://github.com/wailsapp/wails/pull/3887) +- Taskfile refactor by [leaanthony](https://github.com/leaanthony) in + [#3748](https://github.com/wailsapp/wails/pull/3748) +- Upgrade to `go-webview2` v1.0.16 by + [leaanthony](https://github.com/leaanthony) +- Fixed `Screen` type to include `ID` not `Id` by + [etesam913](https://github.com/etesam913) in + [#3778](https://github.com/wailsapp/wails/pull/3778) +- Update `go.mod.tmpl` wails version to support `application.ServiceOptions` by + [northes](https://github.com/northes) in + [#3836](https://github.com/wailsapp/wails/pull/3836) +- Fixed service name determination by [windom](https://github.com/windom/) in + [#3827](https://github.com/wailsapp/wails/pull/3827) +- mkdocs serve now uses docker by [leaanthony](https://github.com/leaanthony) +- Consolidated dev config into `config.yml` by + [leaanthony](https://github.com/leaanthony) +- Systray dialog now defaults to the application icon if available (Windows) by + [@leaanthony](https://github.com/leaanthony) +- Better reporting of GPU + Memory for macOS by + [@leaanthony](https://github.com/leaanthony) +- Removed `WebviewGpuIsDisabled` and `EnableFraudulentWebsiteWarnings` + (superseded by `EnabledFeatures` and `DisabledFeatures` options) by + [leaanthony](https://github.com/leaanthony) + +### Fixed + +- Fixed deadlock in Linux dialog for multiple selections caused by unclosed + channel variable by @michael-freling in + [#3925](https://github.com/wailsapp/wails/pull/3925) +- Fixed cross-platform cleanup for .syso files during Windows build by + [ansxuman](https://github.com/ansxuman) in + [#3924](https://github.com/wailsapp/wails/pull/3924) +- Fixed amd64 appimage compile by @atterpac in + [#3898](https://github.com/wailsapp/wails/pull/3898) +- Fixed build assets update by @ansxuman in + [#3901](https://github.com/wailsapp/wails/pull/3901) +- Fixed Linux systray `OnClick` and `OnRightClick` implementation by @atterpac + in [#3886](https://github.com/wailsapp/wails/pull/3886) +- Fixed `AlwaysOnTop` not working on Mac by + [leaanthony](https://github.com/leaanthony) in + [#3841](https://github.com/wailsapp/wails/pull/3841) +-  Fixed `application.NewEditMenu` including a duplicate + `PasteAndMatchStyle` role in the edit menu on Darwin by + [johnmccabe](https://github.com/johnmccabe) in + [#3839](https://github.com/wailsapp/wails/pull/3839) +- 🐧 Fixed aarch64 compilation + [#3840](https://github.com/wailsapp/wails/issues/3840) in + [#3854](https://github.com/wailsapp/wails/pull/3854) by + [kodflow](https://github.com/kodflow) +- ⊞ Fixed radio group menu items by + [@leaanthony](https://github.com/leaanthony) +- Fix error on building runnable .app on MacOS when 'name' and 'outputfilename' + are different. by @nickisworking in + [#3789](https://github.com/wailsapp/wails/pull/3789) + +## v3.0.0-alpha.7 - 2024-09-18 + +### Added + +- ⊞ New DIP system for Enhanced High DPI Monitor Support by + [mmghv](https://github.com/mmghv) in + [#3665](https://github.com/wailsapp/wails/pull/3665) +- ⊞ Window class name option by [windom](https://github.com/windom/) in + [#3682](https://github.com/wailsapp/wails/pull/3682) +- Services have been expanded to provide plugin functionality. By + [atterpac](https://github.com/atterpac) and + [leaanthony](https://github.com/leaanthony) in + [#3570](https://github.com/wailsapp/wails/pull/3570) + +### Changed + +- Events API change: `On`/`Emit` -> user events, `OnApplicationEvent` -> + Application Events `OnWindowEvent` -> Window Events, by + [leaanthony](https://github.com/leaanthony) +- Fix for Events API on Linux by [TheGB0077](https://github.com/TheGB0077) in + [#3734](https://github.com/wailsapp/wails/pull/3734) +- [CI] improvements to actions & enable to run actions also in forks and + branches prefixed with `v3/` or `v3-` by + [stendler](https://github.com/stendler) in + [#3747](https://github.com/wailsapp/wails/pull/3747) + +### Fixed + +- Fixed bug with usage of customEventProcessor in drag-n-drop example by + [etesam913](https://github.com/etesam913) in + [#3742](https://github.com/wailsapp/wails/pull/3742) +- 🐧 Fixed linux compile error introduced by IgnoreMouseEvents addition by + [atterpac](https://github.com/atterpac) in + [#3721](https://github.com/wailsapp/wails/pull/3721) +- ⊞ Fixed syso icon file generation bug by + [atterpac](https://github.com/atterpac) in + [#3675](https://github.com/wailsapp/wails/pull/3675) +- 🐧 Fix to run natively in wayland incorporated from + [#1811](https://github.com/wailsapp/wails/pull/1811) in + [#3614](https://github.com/wailsapp/wails/pull/3614) by + [@stendler](https://github.com/stendler) +- Do not bind internal service methods in + [#3720](https://github.com/wailsapp/wails/pull/3720) by + [leaanthony](https://github.com/leaanthony) +- ⊞ Fixed system tray startup panic in + [#3693](https://github.com/wailsapp/wails/issues/3693) by + [@DeltaLaboratory](https://github.com/DeltaLaboratory) +- Do not bind internal service methods in + [#3720](https://github.com/wailsapp/wails/pull/3720) by + [leaanthony](https://github.com/leaanthony) +- ⊞ Fixed system tray startup panic in + [#3693](https://github.com/wailsapp/wails/issues/3693) by + [@DeltaLaboratory](https://github.com/DeltaLaboratory) +- Major menu item refactor and event handling. Mainly improves macOS for now. By + [leaanthony](https://github.com/leaanthony) +- Fix tests after plugins and event refactor in + [#3746](https://github.com/wailsapp/wails/pull/3746) by + [@stendler](https://github.com/stendler) +- ⊞ Fixed `Failed to unregister class Chrome_WidgetWin_0` warning. By + [leaanthony](https://github.com/leaanthony) + +## v3.0.0-alpha.6 - 2024-07-30 + +### Fixed + +- Module issues + +## v3.0.0-alpha.5 - 2024-07-30 + +### Added + +- 🐧 WindowDidMove / WindowDidResize events in + [#3580](https://github.com/wailsapp/wails/pull/3580) +- ⊞ WindowDidResize event in + [#3580](https://github.com/wailsapp/wails/pull/3580) +-  add Event ApplicationShouldHandleReopen to be able to handle dock + icon click by @5aaee9 in [#2991](https://github.com/wailsapp/wails/pull/2991) +-  add getPrimaryScreen/getScreens to impl by @tmclane in + [#2618](https://github.com/wailsapp/wails/pull/2618) +-  add option for showing the toolbar in fullscreen mode on macOS by + [@fbbdev](https://github.com/fbbdev) in + [#3282](https://github.com/wailsapp/wails/pull/3282) +- 🐧 add onKeyPress logic to convert linux keypress into an accelerator + @[Atterpac](https://github.com/Atterpac) + in[#3022](https://github.com/wailsapp/wails/pull/3022]) +- 🐧 add task `run:linux` by + [@marcus-crane](https://github.com/marcus-crane) in + [#3146](https://github.com/wailsapp/wails/pull/3146) +- Export `SetIcon` method by [@almas-x](https://github.com/almas-x) in + [PR](https://github.com/wailsapp/wails/pull/3147) +- Improve `OnShutdown` by [@almas-x](https://github.com/almas-x) in + [PR](https://github.com/wailsapp/wails/pull/3189) +- Restore `ToggleMaximise` method in `Window` interface by + [@fbbdev](https://github.com/fbbdev) in + [#3281](https://github.com/wailsapp/wails/pull/3281) +- Added more information to `Environment()`. By @leaanthony in + [aba82cc](https://github.com/wailsapp/wails/commit/aba82cc52787c97fb99afa58b8b63a0004b7ff6c) + based on [PR](https://github.com/wailsapp/wails/pull/2044) by @Mai-Lapyst +- Expose the `WebviewWindow.IsFocused` method on the `Window` interface by + [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- Support multiple space-separated trigger events in the WML system by + [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- Add ESM exports from the bundled JS runtime script by + [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- Add binding generator flag for using the bundled JS runtime script instead of + the npm package by [@fbbdev](https://github.com/fbbdev) in + [#3334](https://github.com/wailsapp/wails/pull/3334) +- Implement `setIcon` on linux by [@abichinger](https://github.com/abichinger) + in [#3354](https://github.com/wailsapp/wails/pull/3354) +- Add flag `-port` to dev command and support environment variable + `WAILS_VITE_PORT` by [@abichinger](https://github.com/abichinger) in + [#3429](https://github.com/wailsapp/wails/pull/3429) +- Add tests for bound method calls by + [@abichinger](https://github.com/abichinger) in + [#3431](https://github.com/wailsapp/wails/pull/3431) +- ⊞ add `SetIgnoreMouseEvents` for already created window by + [@bruxaodev](https://github.com/bruxaodev) in + [#3667](https://github.com/wailsapp/wails/pull/3667) +-  Add ability to set a window's stacking level (order) by + [@OlegGulevskyy](https://github.com/OlegGulevskyy) in + [#3674](https://github.com/wailsapp/wails/pull/3674) + +### Fixed + +- Fixed resize event messaging by [atterpac](https://github.com/atterpac) in + [#3606](https://github.com/wailsapp/wails/pull/3606) +- 🐧Fixed theme handling error on NixOS by + [tmclane](https://github.com/tmclane) in + [#3515](https://github.com/wailsapp/wails/pull/3515) +- Fixed cross volume project install for windows by + [atterpac](https://github.com/atterac) in + [#3512](https://github.com/wailsapp/wails/pull/3512) +- Fixed react template css to show footer by + [atterpac](https://github.com/atterpac) in + [#3477](https://github.com/wailsapp/wails/pull/3477) +- Fixed zombie processes when working in devmode by updating to latest refresh + by [Atterpac](https://github.com/atterpac) in + [#3320](https://github.com/wailsapp/wails/pull/3320). +- Fixed appimage webkit file sourcing by [Atterpac](https://github.com/atterpac) + in [#3306](https://github.com/wailsapp/wails/pull/3306). +- Fixed Doctor apt package verify by [Atterpac](https://github.com/Atterpac) in + [#2972](https://github.com/wailsapp/wails/pull/2972). +- Fixed application frozen when quit (Darwin) by @5aaee9 in + [#2982](https://github.com/wailsapp/wails/pull/2982) +- Fixed background colours of examples on Windows by + [mmghv](https://github.com/mmghv) in + [#2750](https://github.com/wailsapp/wails/pull/2750). +- Fixed default context menus by [mmghv](https://github.com/mmghv) in + [#2753](https://github.com/wailsapp/wails/pull/2753). +- Fixed hex values for arrow keys on Darwin by + [jaybeecave](https://github.com/jaybeecave) in + [#3052](https://github.com/wailsapp/wails/pull/3052). +- Set drag-n-drop for windows to working. Added by + [@pylotlight](https://github.com/pylotlight) in + [PR](https://github.com/wailsapp/wails/pull/3039) +- Fixed bug for linux in doctor in the event user doesn't have proper drivers + installed. Added by [@pylotlight](https://github.com/pylotlight) in + [PR](https://github.com/wailsapp/wails/pull/3032) +- Fix dpi scaling on start up (windows). Changed by [@almas-x](https://github.com/almas-x) in + [PR](https://github.com/wailsapp/wails/pull/3145) +- Fix replace line in `go.mod` to use relative paths. Fixes Windows paths with + spaces - @leaanthony. +- Fix MacOS systray click handling when no attached window by + [thomas-senechal](https://github.com/thomas-senechal) in PR + [#3207](https://github.com/wailsapp/wails/pull/3207) +- Fix failing Windows build due to unknown option by + [thomas-senechal](https://github.com/thomas-senechal) in PR + [#3208](https://github.com/wailsapp/wails/pull/3208) +- Fix crash on windows left clicking the systray icon when not having an + attached window [tw1nk](https://github.com/tw1nk) in PR + [#3271](https://github.com/wailsapp/wails/pull/3271) +- Fix wrong baseURL when open window twice by @5aaee9 in PR + [#3273](https://github.com/wailsapp/wails/pull/3273) +- Fix ordering of if branches in `WebviewWindow.Restore` method by + [@fbbdev](https://github.com/fbbdev) in + [#3279](https://github.com/wailsapp/wails/pull/3279) +- Correctly compute `startURL` across multiple `GetStartURL` invocations when + `FRONTEND_DEVSERVER_URL` is present. + [#3299](https://github.com/wailsapp/wails/pull/3299) +- Fix the JS type of the `Screen` struct to match its Go counterpart by + [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- Fix the `WML.Reload` method to ensure proper cleanup of registered event + listeners by [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- Fix custom context menu closing immediately on linux by + [@abichinger](https://github.com/abichinger) in + [#3330](https://github.com/wailsapp/wails/pull/3330) +- Fix the output path and extension of model files produced by the binding + generator by [@fbbdev](https://github.com/fbbdev) in + [#3334](https://github.com/wailsapp/wails/pull/3334) +- Fix the import paths of model files in JS code produced by the binding + generator by [@fbbdev](https://github.com/fbbdev) in + [#3334](https://github.com/wailsapp/wails/pull/3334) +- Fix drag-n-drop on some linux distros by + [@abichinger](https://github.com/abichinger) in + [#3346](https://github.com/wailsapp/wails/pull/3346) +- Fix missing task for macOS when using `wails3 task dev` by + [@hfoxy](https://github.com/hfoxy) in + [#3417](https://github.com/wailsapp/wails/pull/3417) +- Fix registering events causing a nil map assignment by + [@hfoxy](https://github.com/hfoxy) in + [#3426](https://github.com/wailsapp/wails/pull/3426) +- Fix unmarshaling of bound method parameters by + [@fbbdev](https://github.com/fbbdev) in + [#3431](https://github.com/wailsapp/wails/pull/3431) +- Fix handling of multiple return values from bound methods by + [@fbbdev](https://github.com/fbbdev) in + [#3431](https://github.com/wailsapp/wails/pull/3431) +- Fix doctor detection of npm that is not installed with system package manager + by [@pekim](https://github.com/pekim) in + [#3458](https://github.com/wailsapp/wails/pull/3458) +- Fix missing MicrosoftEdgeWebview2Setup.exe. Thanks to + [@robin-samuel](https://github.com/robin-samuel). +- Fix random crash on linux due to window ID handling by @leaanthony. Based on + PR [#3466](https://github.com/wailsapp/wails/pull/3622) by + [@5aaee9](https://github.com/5aaee9). +- Fix systemTray.setIcon crashing on Linux by + [@windom](https://github.com/windom/) in + [#3636](https://github.com/wailsapp/wails/pull/3636). +- Fix Ensure Window Frame is Applied on First Call in `setFrameless` Function on + Windows by [@bruxaodev](https://github.com/bruxaodev/) in + [#3691](https://github.com/wailsapp/wails/pull/3691). + +### Changed + +- Renamed `AbsolutePosition()` to `Position()` by + [mmghv](https://github.com/mmghv) in + [#3611](https://github.com/wailsapp/wails/pull/3611) +- Update linux webkit dependency to webkit2gtk-4.1 over webkitgtk2-4.0 to + support Ubuntu 24.04 LTS by [atterpac](https://github.com/atterpac) in + [#3461](https://github.com/wailsapp/wails/pull/3461) +- The bundled JS runtime script is now an ESM module: script tags importing it + must have the `type="module"` attribute. By + [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- The `@wailsio/runtime` package does not publish its API on the `window.wails` + object, and does not start the WML system. This has been done to improve + encapsulation. The WML system can be started manually if desired by calling + the new `WML.Enable` method. The bundled JS runtime script still performs both + operations automatically. By [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- The Window API module `@wailsio/runtime/src/window` now exposes the containing + window object as a default export. It is not possible anymore to import + individual methods through ESM named or namespace import syntax. +- The JS window API has been updated to match the current Go `WebviewWindow` + API. Some methods have changed name or prototype, specifically: `Screen` + becomes `GetScreen`; `GetZoomLevel`/`SetZoomLevel` become `GetZoom`/`SetZoom`; + `GetZoom`, `Width` and `Height` now return values directly instead of wrapping + them within objects. By [@fbbdev](https://github.com/fbbdev) in + [#3295](https://github.com/wailsapp/wails/pull/3295) +- The binding generator now uses calls by ID by default. The `-id` CLI option + has been removed. Use the `-names` CLI option to switch back to calls by name. + By [@fbbdev](https://github.com/fbbdev) in + [#3468](https://github.com/wailsapp/wails/pull/3468) +- New binding code layout: output files were previously organised in folders + named after their containing package; now full Go import paths are used, + including the module path. By [@fbbdev](https://github.com/fbbdev) in + [#3468](https://github.com/wailsapp/wails/pull/3468) +- The struct field `application.Options.Bind` has been renamed to + `application.Options.Services`. By [@fbbdev](https://github.com/fbbdev) in + [#3468](https://github.com/wailsapp/wails/pull/3468) +- New syntax for binding services: service instances must now be wrapped in a + call to `application.NewService`. By [@fbbdev](https://github.com/fbbdev) in + [#3468](https://github.com/wailsapp/wails/pull/3468) +- Disable spinner on Non-Terminal or CI Environment by + [@DeltaLaboratory](https://github.com/DeltaLaboratory) in + [#3574](https://github.com/wailsapp/wails/pull/3574) diff --git a/docs/src/content/docs/community/links.md b/docs/src/content/docs/community/links.md new file mode 100644 index 000000000..9507991dd --- /dev/null +++ b/docs/src/content/docs/community/links.md @@ -0,0 +1,27 @@ +--- +title: Links +--- + +This page serves as a list for community related links. + +:::tip[How to Submit a Link] + +You can click the `Edit page` at the bottom of this page to submit a PR. + +::: + +## Awesome Wails + +The [definitive list](https://github.com/wailsapp/awesome-wails) of links +related to Wails. + +## Support Channels + +- [Wails Discord Server](https://discord.gg/bdj28QNHmT) +- [Github Issues](https://github.com/wailsapp/wails/issues) + +## Social Media + +- [Twitter](https://x.com/wailsapp) +- [Wails Chinese Community QQ Group](https://qm.qq.com/cgi-bin/qm/qr?k=PmIURne5hFGNd7QWzW5qd6FV-INEjNJv&jump_from=webapi) - + Group number: 1067173054 diff --git a/docs/src/content/docs/community/showcase/_template.md b/docs/src/content/docs/community/showcase/_template.md new file mode 100644 index 000000000..2ad8aab68 --- /dev/null +++ b/docs/src/content/docs/community/showcase/_template.md @@ -0,0 +1,13 @@ +--- +title: My Project +--- + + + +![My Project](../../../../assets/showcase-images/my-project.webp) + + + + + +[My Project](https://my-project.com) diff --git a/docs/src/content/docs/community/showcase/bulletinboard.md b/docs/src/content/docs/community/showcase/bulletinboard.md new file mode 100644 index 000000000..58d24b97f --- /dev/null +++ b/docs/src/content/docs/community/showcase/bulletinboard.md @@ -0,0 +1,16 @@ +--- +title: BulletinBoard +--- + +![BulletinBoard](../../../../assets/showcase-images/bboard.webp) + +The [BulletinBoard](https://github.com/raguay/BulletinBoard) application is a +versital message board for static messages or dialogs to get information from +the user for a script. It has a TUI for creating new dialogs that can latter be +used to get information from the user. It's design is to stay running on your +system and show the information as needed and then hide away. I have a process +for watching a file on my system and sending the contents to BulletinBoard when +changed. It works great with my workflows. T here is also an +[Alfred workflow](https://github.com/raguay/MyAlfred/blob/master/Alfred%205/EmailIt.alfredworkflow) +for sending information to the program. The workflow is also for working with +[EmailIt](https://github.com/raguay/EmailIt). diff --git a/docs/src/content/docs/community/showcase/cfntracker.md b/docs/src/content/docs/community/showcase/cfntracker.md new file mode 100644 index 000000000..dc9c030f3 --- /dev/null +++ b/docs/src/content/docs/community/showcase/cfntracker.md @@ -0,0 +1,36 @@ +--- +title: CFN Tracker +--- + +![CFN Tracker](../../../../assets/showcase-images/cfntracker.webp) + +[CFN Tracker](https://github.com/williamsjokvist/cfn-tracker) - Track any Street +Fighter 6 or V CFN profile's live matches. Check +[the website](https://cfn.williamsjokvist.se/) to get started. + +## Features + +- Real-time match tracking +- Storing match logs and statistics +- Support for displaying live stats to OBS via Browser Source +- Support for both SF6 and SFV +- Ability for users to create their own OBS Browser themes with CSS + +### Major tech used alongside Wails + +- [Task](https://github.com/go-task/task) - wrapping the Wails CLI to make + common commands easy to use +- [React](https://github.com/facebook/react) - chosen for its rich ecosystem + (radix, framer-motion) +- [Bun](https://github.com/oven-sh/bun) - used for its fast dependency + resolution and build-time +- [Rod](https://github.com/go-rod/rod) - headless browser automation for + authentication and polling changes +- [SQLite](https://github.com/mattn/go-sqlite3) - used for storing matches, + sessions and profiles +- [Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) - + a http stream to send tracking updates to OBS browser sources +- [i18next](https://github.com/i18next/) - with backend connector to serve + localization objects from the Go layer +- [xstate](https://github.com/statelyai/xstate) - state machines for auth + process and tracking diff --git a/docs/src/content/docs/community/showcase/clave.md b/docs/src/content/docs/community/showcase/clave.md new file mode 100644 index 000000000..9c405e616 --- /dev/null +++ b/docs/src/content/docs/community/showcase/clave.md @@ -0,0 +1,18 @@ +--- +title: Clave +--- + +![Clave](../../../../assets/showcase-images/clave.png) + +Key Features + +- 🎨 Simple, intuitive design for hassle-free experience +- ✅ Add accounts easily via manual entry or QR code image import. +- 🔒 End-to-end encryption with PIN or Touch ID(macOS only). +- 💻 Available for macOS, Windows, and Linux +- ⚡ Quick access from system tray for convenience +- 📂 Easily backup and restore your profiles + +Try Clave Today! + +💻 [https://clave.rocks](https://clave.rocks) diff --git a/docs/src/content/docs/community/showcase/emailit.md b/docs/src/content/docs/community/showcase/emailit.md new file mode 100644 index 000000000..6afc1da5e --- /dev/null +++ b/docs/src/content/docs/community/showcase/emailit.md @@ -0,0 +1,14 @@ +--- +title: EmailIt +--- + +![EmailIt](../../../../assets/showcase-images/emailit.webp) + +[EmailIt](https://github.com/raguay/EmailIt/) is a Wails 2 program that is a +markdown based email sender only with nine notepads, scripts to manipulate the +text, and templates. It also has a scripts terminal to run scripts in EmailIt on +files in your system. The scripts and templates can be used from the commandline +itself or with the Alfred, Keyboard Maestro, Dropzone, or PopClip extensions. It +also supports scripts and themes downloaded form GitHub. Documentation is not +complete, but the programs works. It’s built using Wails2 and Svelte, and the +download is a universal macOS application. diff --git a/docs/src/content/docs/community/showcase/encrypteasy.md b/docs/src/content/docs/community/showcase/encrypteasy.md new file mode 100644 index 000000000..3a098fac9 --- /dev/null +++ b/docs/src/content/docs/community/showcase/encrypteasy.md @@ -0,0 +1,16 @@ +--- +title: EncryptEasy +--- + +![EncryptEasy](../../../../assets/showcase-images/encrypteasy.webp) + +**[EncryptEasy](https://www.encrypteasy.app) is a simple and easy to use PGP +encryption tool, managing all your and your contacts keys. Encryption should be +simple. Developed with Wails.** + +Encrypting messages using PGP is the industry standard. Everyone has a private +and a public key. Your private key, well, needs to be kept private so only you +can read messages. Your public key is distributed to anyone who wants to send +you secret, encrypted messages. Managing keys, encrypting messages and +decrypting messages should be a smooth experience. EncryptEasy is all about +making it easy. diff --git a/docs/src/content/docs/community/showcase/espstudio.md b/docs/src/content/docs/community/showcase/espstudio.md new file mode 100644 index 000000000..0ac90a827 --- /dev/null +++ b/docs/src/content/docs/community/showcase/espstudio.md @@ -0,0 +1,9 @@ +--- +title: ESP Studio +--- + +![ESP Studio](../../../../assets/showcase-images/esp-studio.png) + +[ESP Studio](https://github.com/torabian/esp-studio) - Cross platform, Desktop, +Cloud, and Embedded software for controlling ESP/Arduino devices, and building +complex IOT workflows and control systems diff --git a/docs/src/content/docs/community/showcase/filehound.md b/docs/src/content/docs/community/showcase/filehound.md new file mode 100644 index 000000000..627007f97 --- /dev/null +++ b/docs/src/content/docs/community/showcase/filehound.md @@ -0,0 +1,29 @@ +--- +title: FileHound Export Utility +--- + +![FileHound Export Utility](../../../../assets/showcase-images/filehound.webp) + +[FileHound Export Utility](https://www.filehound.co.uk/) FileHound is a cloud +document management platform made for secure file retention, business process +automation and SmartCapture capabilities. + +The FileHound Export Utility allows FileHound Administrators the ability to run +a secure document and data extraction tasks for alternative back-up and recovery +purposes. This application will download all documents and/or meta data saved in +FileHound based on the filters you choose. The metadata will be exported in both +JSON and XML formats. + +Backend built with: + +- Go 1.15 +- Wails 1.11.0 +- go-sqlite3 1.14.6 +- go-linq 3.2 + +Frontend with: + +- Vue 2.6.11 +- Vuex 3.4.0 +- TypeScript +- Tailwind 1.9.6 diff --git a/docs/src/content/docs/community/showcase/hiposter.md b/docs/src/content/docs/community/showcase/hiposter.md new file mode 100644 index 000000000..08acfa832 --- /dev/null +++ b/docs/src/content/docs/community/showcase/hiposter.md @@ -0,0 +1,8 @@ +--- +title: hiposter +--- + +![hiposter](../../../../assets/showcase-images/hiposter.webp) + +[hiposter](https://github.com/obity/hiposter) is a simple and efficient http API +testing client tool. Based on Wails, Go and sveltejs. diff --git a/docs/src/content/docs/community/showcase/index.mdx b/docs/src/content/docs/community/showcase/index.mdx new file mode 100644 index 000000000..e1da3f75d --- /dev/null +++ b/docs/src/content/docs/community/showcase/index.mdx @@ -0,0 +1,189 @@ +--- +title: Overview +sidebar: + order: 1 +--- + +import { ShowcaseImage } from "starlight-showcases"; +import { Steps } from "@astrojs/starlight/components"; + +:::tip[See how to add your project] + +Check out the +[How to add my project in showcase](#how-to-add-my-project-in-showcase) section. + +::: + + + +## How to add my project in showcase + + + +1. Make a fork of the repository. +2. Add the image(s) under `docs/src/assets/showcase-images` folder. +3. Make a copy of the `_template.md` file under + `docs/src/content/docs/community/showcase` folder. +4. Rename the copied file to the name of your project. (Name should not start + with `_`)) +5. Update the title, image, link and content of the file. +6. Add it on the above list in + `docs/src/content/docs/community/showcase/index.mdx`. +7. Submit a PR. + + diff --git a/docs/src/content/docs/community/showcase/mchat.md b/docs/src/content/docs/community/showcase/mchat.md new file mode 100644 index 000000000..444aed343 --- /dev/null +++ b/docs/src/content/docs/community/showcase/mchat.md @@ -0,0 +1,8 @@ +--- +title: Mchat +--- + +![Mchat](../../../../assets/showcase-images/mchat.png) + +[Official page](https://marcio199226.github.io/mchat-site/public/) Fully +anonymous end2end encrypted chat. diff --git a/docs/src/content/docs/community/showcase/minecraftupdater.md b/docs/src/content/docs/community/showcase/minecraftupdater.md new file mode 100644 index 000000000..57cd80e30 --- /dev/null +++ b/docs/src/content/docs/community/showcase/minecraftupdater.md @@ -0,0 +1,10 @@ +--- +title: Minecraft Updater +--- + +![Minecraft Updater](../../../../assets/showcase-images/minecraft-mod-updater.webp) + +[Minecraft Updater](https://github.com/Gurkengewuerz/MinecraftModUpdater) is a +utility tool to update and synchronize Minecraft mods for your userbase. It’s +built using Wails2 and React with [antd](https://ant.design/) as frontend +framework. diff --git a/docs/src/content/docs/community/showcase/minesweeper-xp.md b/docs/src/content/docs/community/showcase/minesweeper-xp.md new file mode 100644 index 000000000..9ca30ed55 --- /dev/null +++ b/docs/src/content/docs/community/showcase/minesweeper-xp.md @@ -0,0 +1,8 @@ +--- +title: Minesweeper XP +--- + +![Minesweeper XP](../../../../assets/showcase-images/minesweeper-xp.webp) + +[Minesweeper-XP](https://git.new/Minesweeper-XP) allows you to experience the +classic Minesweeper XP (+ 98 and 3.1) on macOS, Windows, and Linux! diff --git a/docs/src/content/docs/community/showcase/modalfilemanager.md b/docs/src/content/docs/community/showcase/modalfilemanager.md new file mode 100644 index 000000000..7a7748d4d --- /dev/null +++ b/docs/src/content/docs/community/showcase/modalfilemanager.md @@ -0,0 +1,21 @@ +--- +title: Modal File Manager +--- + +![Modal File Manager](../../../../assets/showcase-images/modalfilemanager.webp) + +[Modal File Manager](https://github.com/raguay/ModalFileManager) is a dual pane +file manager using web technologies. My original design was based on NW.js and +can be found [here](https://github.com/raguay/ModalFileManager-NWjs). This +version uses the same Svelte based frontend code (but it has be greatly modified +since the departure from NW.js), but the backend is a +[Wails 2](https://wails.io/) implementation. By using this implementation, I no +longer use command line `rm`, `cp`, etc. commands, but a git install has to be +on the system to download themes and extensions. It is fully coded using Go and +runs much faster than the previous versions. + +This file manager is designed around the same principle as Vim: a state +controlled keyboard actions. The number of states isn't fixed, but very +programmable. Therefore, an infinite number of keyboard configurations can be +created and used. This is the main difference from other file managers. There +are themes and extensions available to download from GitHub. diff --git a/docs/src/content/docs/community/showcase/mollywallet.md b/docs/src/content/docs/community/showcase/mollywallet.md new file mode 100644 index 000000000..930aebd75 --- /dev/null +++ b/docs/src/content/docs/community/showcase/mollywallet.md @@ -0,0 +1,9 @@ +--- +title: Molley Wallet +--- + +![Molly Wallet](../../../../assets/showcase-images/mollywallet.webp) + +[Molly Wallet](https://github.com/grvlle/constellation_wallet/) the official +$DAG wallet of the Constellation Network. It'll let users interact with the +Hypergraph Network in various ways, not limited to producing $DAG transactions. diff --git a/docs/src/content/docs/community/showcase/october.md b/docs/src/content/docs/community/showcase/october.md new file mode 100644 index 000000000..60a29e91b --- /dev/null +++ b/docs/src/content/docs/community/showcase/october.md @@ -0,0 +1,16 @@ +--- +title: October +--- + +![October](../../../../assets/showcase-images/october.webp) + +[October](https://october.utf9k.net) is a small Wails application that makes it +really easy to extract highlights from +[Kobo eReaders](https://en.wikipedia.org/wiki/Kobo_eReader) and then forward +them to [Readwise](https://readwise.io). + +It has a relatively small scope with all platform versions weighing in under +10MB, and that's without enabling [UPX compression](https://upx.github.io/)! + +In contrast, the author's previous attempts with Electron quickly bloated to +several hundred megabytes. diff --git a/docs/src/content/docs/community/showcase/optimus.md b/docs/src/content/docs/community/showcase/optimus.md new file mode 100644 index 000000000..6128258b2 --- /dev/null +++ b/docs/src/content/docs/community/showcase/optimus.md @@ -0,0 +1,9 @@ +--- +title: Optimus +--- + +![Optimus](../../../../assets/showcase-images/optimus.webp) + +[Optimus](https://github.com/splode/optimus) is a desktop image optimization +application. It supports conversion and compression between WebP, JPEG, and PNG +image formats. diff --git a/docs/src/content/docs/community/showcase/portfall.md b/docs/src/content/docs/community/showcase/portfall.md new file mode 100644 index 000000000..c1ffc76f9 --- /dev/null +++ b/docs/src/content/docs/community/showcase/portfall.md @@ -0,0 +1,8 @@ +--- +title: Portfall +--- + +![Portfall](../../../../assets/showcase-images/portfall.webp) + +[Portfall](https://github.com/rekon-oss/portfall) - A desktop k8s +port-forwarding portal for easy access to all your cluster UIs diff --git a/docs/src/content/docs/community/showcase/resizem.md b/docs/src/content/docs/community/showcase/resizem.md new file mode 100644 index 000000000..3e8761dbc --- /dev/null +++ b/docs/src/content/docs/community/showcase/resizem.md @@ -0,0 +1,9 @@ +--- +title: Resizem +--- + +![Resizem Screenshot](../../../../assets/showcase-images/resizem.webp) + +[Resizem](https://github.com/barats/resizem) - is an app designed for bulk image +process. It is particularly useful for users who need to resize, convert, and +manage large numbers of image files at once. diff --git a/docs/src/content/docs/community/showcase/restic-browser.md b/docs/src/content/docs/community/showcase/restic-browser.md new file mode 100644 index 000000000..5a68cb02e --- /dev/null +++ b/docs/src/content/docs/community/showcase/restic-browser.md @@ -0,0 +1,9 @@ +--- +title: Restic Browser +--- + +![Restic Browser](../../../../assets/showcase-images/restic-browser-2.png) + +[Restic-Browser](https://github.com/emuell/restic-browser) - A simple, +cross-platform [restic](https://github.com/restic/restic) backup GUI for +browsing and restoring restic repositories. diff --git a/docs/src/content/docs/community/showcase/riftshare.md b/docs/src/content/docs/community/showcase/riftshare.md new file mode 100644 index 000000000..b6ed8f464 --- /dev/null +++ b/docs/src/content/docs/community/showcase/riftshare.md @@ -0,0 +1,23 @@ +--- +title: RiftShare +--- + +![RiftShare](../../../../assets/showcase-images/riftshare-main.webp) + +Easy, Secure, and Free file sharing for everyone. Learn more at +[Riftshare.app](https://riftshare.app) + +## Features + +- Easy secure file sharing between computers both in the local network and + through the internet +- Supports sending files or directories securely through the + [magic wormhole protocol](https://magic-wormhole.readthedocs.io/en/latest/) +- Compatible with all other apps using magic wormhole (magic-wormhole or + wormhole-william CLI, wormhole-gui, etc.) +- Automatic zipping of multiple selected files to send at once +- Full animations, progress bar, and cancellation support for sending and + receiving +- Native OS File Selection +- Open files in one click once received +- Auto Update - don't worry about having the latest release! diff --git a/docs/src/content/docs/community/showcase/scriptbar.md b/docs/src/content/docs/community/showcase/scriptbar.md new file mode 100644 index 000000000..1e03ab528 --- /dev/null +++ b/docs/src/content/docs/community/showcase/scriptbar.md @@ -0,0 +1,14 @@ +--- +title: ScriptBar +--- + +![ScriptBar](../../../../assets/showcase-images/scriptbar.webp) + +[ScriptBar](https://GitHub.com/raguay/ScriptBarApp) is a program to show the +output of scripts or [Node-Red](https://nodered.org) server. It runs scripts +defined in EmailIt program and shows the output. Scripts from xBar or TextBar +can be used, but currently on the TextBar scripts work well. It also displays +the output of scripts on your system. ScriptBar doesn't put them in the menubar, +but has them all in a convient window for easy viewing. You can have multiple +tabs to have many different things show. You can also keep the links to your +most visited web sites. diff --git a/docs/src/content/docs/community/showcase/snippetexpander.md b/docs/src/content/docs/community/showcase/snippetexpander.md new file mode 100644 index 000000000..0b5a88801 --- /dev/null +++ b/docs/src/content/docs/community/showcase/snippetexpander.md @@ -0,0 +1,34 @@ +--- +title: Snippet Expander +--- + +![Snippet Expander Screenshot](../../../../assets/showcase-images/snippetexpandergui-select-snippet.png) + +Screenshot of Snippet Expander's Select Snippet window + +![Snippet Expander Screenshot](../../../../assets/showcase-images/snippetexpandergui-add-snippet.png) + +Screenshot of Snippet Expander's Add Snippet screen + +![Snippet Expander Screenshot](../../../../assets/showcase-images/snippetexpandergui-search-and-paste.png) + +Screenshot of Snippet Expander's Search & Paste window + +[Snippet Expander](https://snippetexpander.org) is "Your little expandable text +snippets helper", for Linux. + +Snippet Expander comprises of a GUI application built with Wails for managing +snippets and settings, with a Search & Paste window mode for quickly selecting +and pasting a snippet. + +The Wails based GUI, go-lang CLI and vala-lang auto expander daemon all +communicate with a go-lang daemon via D-Bus. The daemon does the majority of the +work, managing the database of snippets and common settings, and providing +services for expanding and pasting snippets etc. + +Check out the +[source code](https://git.sr.ht/~ianmjones/snippetexpander/tree/trunk/item/cmd/snippetexpandergui/app.go#L38) +to see how the Wails app sends messages from the UI to the backend that are then +sent to the daemon, and subscribes to a D-Bus event to monitor changes to +snippets via another instance of the app or CLI and show them instantly in the +UI via a Wails event. diff --git a/docs/src/content/docs/community/showcase/surge.md b/docs/src/content/docs/community/showcase/surge.md new file mode 100644 index 000000000..3ab4444dd --- /dev/null +++ b/docs/src/content/docs/community/showcase/surge.md @@ -0,0 +1,9 @@ +--- +title: Surge +--- + +![Surge Screenshot](../../../../assets/showcase-images/surge.png) + +[Surge](https://getsurge.io/) is a p2p filesharing app designed to utilize +blockchain technologies to enable 100% anonymous file transfers. Surge is +end-to-end encrypted, decentralized and open source. diff --git a/docs/src/content/docs/community/showcase/tinyrdm.md b/docs/src/content/docs/community/showcase/tinyrdm.md new file mode 100644 index 000000000..f9551e046 --- /dev/null +++ b/docs/src/content/docs/community/showcase/tinyrdm.md @@ -0,0 +1,12 @@ +--- +title: Tiny RDM +--- + +![Tiny RDM Screenshot](../../../../assets/showcase-images/tiny-rdm1.webp) +![Tiny RDM Screenshot](../../../../assets/showcase-images/tiny-rdm2.webp) + +The [Tiny RDM](https://redis.tinycraft.cc/) application is an open-source, +modern lightweight Redis GUI. It has a beautful UI, intuitive Redis database +management, and compatible with Windows, Mac, and Linux. It provides visual +key-value data operations, supports various data decoding and viewing options, +built-in console for executing commands, slow log queries and more. diff --git a/docs/src/content/docs/community/showcase/wailsterm.md b/docs/src/content/docs/community/showcase/wailsterm.md new file mode 100644 index 000000000..0521aac70 --- /dev/null +++ b/docs/src/content/docs/community/showcase/wailsterm.md @@ -0,0 +1,8 @@ +--- +title: WailsTerm +--- + +![WailsTerm Screenshot](../../../../assets/showcase-images/wailsterm.webp) + +[WailsTerm](https://github.com/rlshukhov/wailsterm) is a simple translucent +terminal app powered by Wails and Xterm.js. diff --git a/docs/src/content/docs/community/showcase/wally.md b/docs/src/content/docs/community/showcase/wally.md new file mode 100644 index 000000000..3f4ddbfa2 --- /dev/null +++ b/docs/src/content/docs/community/showcase/wally.md @@ -0,0 +1,10 @@ +--- +title: Wally +--- + +![Wally Screenshot](../../../../assets/showcase-images/wally.webp) + +[Wally](https://ergodox-ez.com/pages/wally) is the official firmware flasher for +[Ergodox](https://ergodox-ez.com/) keyboards. It looks great and is a fantastic +example of what you can achieve with Wails: the ability to combine the power of +Go and the rich graphical tools of the web development world. diff --git a/docs/src/content/docs/community/showcase/warmine.md b/docs/src/content/docs/community/showcase/warmine.md new file mode 100644 index 000000000..19de97f2a --- /dev/null +++ b/docs/src/content/docs/community/showcase/warmine.md @@ -0,0 +1,17 @@ +--- +title: Minecraft launcher for WarMine +--- + +![WarMine Screenshot](../../../../assets/showcase-images/warmine1.png) +![WarMine Screenshot](../../../../assets/showcase-images/warmine2.png) + +[Minecraft launcher for WarMine](https://warmine.ru/) is a Wails application, +that allows you to easily join modded game servers and manage your game +accounts. + +The Launcher downloads the game files, checks their integrity and launches the +game with a wide range of customization options for the launch arguments from +the backend. + +Frontend is written in Svelte, whole launcher fits in 9MB and supports Windows +7-11. diff --git a/docs/src/content/docs/community/showcase/wombat.md b/docs/src/content/docs/community/showcase/wombat.md new file mode 100644 index 000000000..d61bdd6c9 --- /dev/null +++ b/docs/src/content/docs/community/showcase/wombat.md @@ -0,0 +1,7 @@ +--- +title: Wombat +--- + +![Wombat Screenshot](../../../../assets/showcase-images/wombat.webp) + +[Wombat](https://github.com/rogchap/wombat) is a cross platform gRPC client. diff --git a/docs/src/content/docs/community/showcase/ytd.md b/docs/src/content/docs/community/showcase/ytd.md new file mode 100644 index 000000000..bbcd6979f --- /dev/null +++ b/docs/src/content/docs/community/showcase/ytd.md @@ -0,0 +1,10 @@ +--- +title: Ytd +--- + +![Ytd Screenshot](../../../../assets/showcase-images/ytd.webp) + +[Ytd](https://github.com/marcio199226/ytd/tree/v2-wails) is an app for +downloading tracks from youtube, creating offline playlists and share them with +your friends, your friends will be able to playback your playlists or download +them for offline listening, has an built-in player. diff --git a/docs/src/content/docs/community/templates.md b/docs/src/content/docs/community/templates.md new file mode 100644 index 000000000..855b70c23 --- /dev/null +++ b/docs/src/content/docs/community/templates.md @@ -0,0 +1,132 @@ +--- +title: Templates +--- + +:::caution + +This page might be outdated for Wails v3. + +::: + + + +This page serves as a list for community supported templates. To build your own +template, please see the [Templates](https://wails.io/docs/guides/templates) +guide. + +:::tip[How to Submit a Template] + +You can click `Edit this page` at the bottom to include your templates. + +::: + +To use these templates, run +`wails init -n "Your Project Name" -t [the link below[@version]]` + +If there is no version suffix, the main branch code template is used by default. +If there is a version suffix, the code template corresponding to the tag of this +version is used. + +Example: +`wails init -n "Your Project Name" -t https://github.com/misitebao/wails-template-vue` + +:::danger[Attention] + +**The Wails project does not maintain, is not responsible nor liable for 3rd +party templates!** + +If you are unsure about a template, inspect `package.json` and `wails.json` for +what scripts are run and what packages are installed. + +::: + +## Vue + +- [wails-template-vue](https://github.com/misitebao/wails-template-vue) - Wails + template based on Vue ecology (Integrated TypeScript, Dark theme, + Internationalization, Single page routing, TailwindCSS) +- [wails-template-quasar-js](https://github.com/sgosiaco/wails-template-quasar-js) - + A template using JavaScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, + Prettier) +- [wails-template-quasar-ts](https://github.com/sgosiaco/wails-template-quasar-ts) - + A template using TypeScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, + Prettier, Composition API with <script setup>) +- [wails-template-naive](https://github.com/tk103331/wails-template-naive) - + Wails template based on Naive UI (A Vue 3 Component Library) +- [wails-template-nuxt](https://github.com/gornius/wails-template-nuxt) - Wails + template using clean Nuxt3 and TypeScript with auto-imports for wails js + runtime +- [Wails-Tool-Template](https://github.com/xisuo67/Wails-Tool-Template) - Wails + template using Vue+TypeScript+Vite+Element-plus(仿网易云) + +## Angular + +- [wails-template-angular](https://github.com/mateothegreat/wails-template-angular) - + Angular 15+ action packed & ready to roll to production. +- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - + Angular with TypeScript, Sass, Hot-Reload, Code-Splitting and i18n + +## React + +- [wails-react-template](https://github.com/AlienRecall/wails-react-template) - + A template using reactjs +- [wails-react-template](https://github.com/flin7/wails-react-template) - A + minimal template for React that supports live development +- [wails-template-nextjs](https://github.com/LGiki/wails-template-nextjs) - A + template using Next.js and TypeScript +- [wails-template-nextjs-app-router](https://github.com/thisisvk-in/wails-template-nextjs-app-router) - + A template using Next.js and TypeScript with App router +- [wails-template-nextjs-app-router-src](https://github.com/edai-git/wails-template-nextjs-app-router) - + A template using Next.js and TypeScript with App router src + example +- [wails-vite-react-ts-tailwind-template](https://github.com/hotafrika/wails-vite-react-ts-tailwind-template) - + A template for React + TypeScript + Vite + TailwindCSS +- [wails-vite-react-ts-tailwind-shadcnui-template](https://github.com/Mahcks/wails-vite-react-tailwind-shadcnui-ts) - + A template with Vite, React, TypeScript, TailwindCSS, and shadcn/ui + +## Svelte + +- [wails-svelte-template](https://github.com/raitonoberu/wails-svelte-template) - + A template using Svelte +- [wails-vite-svelte-template](https://github.com/BillBuilt/wails-vite-svelte-template) - + A template using Svelte and Vite +- [wails-vite-svelte-tailwind-template](https://github.com/BillBuilt/wails-vite-svelte-tailwind-template) - + A template using Svelte and Vite with TailwindCSS v3 +- [wails-svelte-tailwind-vite-template](https://github.com/PylotLight/wails-vite-svelte-tailwind-template/tree/master) - + An updated template using Svelte v4.2.0 and Vite with TailwindCSS v3.3.3 +- [wails-sveltekit-template](https://github.com/h8gi/wails-sveltekit-template) - + A template using SvelteKit +- [wails-template-shadcn-svelte](https://github.com/xijaja/wails-template-shadcn-svelte) - + A template using Sveltekit and Shadcn-Svelte + +## Solid + +- [wails-template-vite-solid-ts](https://github.com/xijaja/wails-template-solid-ts) - + A template using Solid + Ts + Vite +- [wails-template-vite-solid-js](https://github.com/xijaja/wails-template-solid-js) - + A template using Solid + Js + Vite + +## Elm + +- [wails-elm-template](https://github.com/benjamin-thomas/wails-elm-template) - + Develop your GUI app with functional programming and a **snappy** hot-reload + setup :tada: :rocket: +- [wails-template-elm-tailwind](https://github.com/rnice01/wails-template-elm-tailwind) - + Combine the powers :muscle: of Elm + Tailwind CSS + Wails! Hot reloading + supported. + +## HTMX + +- [wails-htmx-templ-chi-tailwind](https://github.com/PylotLight/wails-hmtx-templ-template) - + Use a unique combination of pure htmx for interactivity plus templ for + creating components and forms + +## Pure JavaScript (Vanilla) + +- [wails-pure-js-template](https://github.com/KiddoV/wails-pure-js-template) - A + template with nothing but just basic JavaScript, HTML, and CSS + +## Lit (web components) + +- [wails-lit-shoelace-esbuild-template](https://github.com/Braincompiler/wails-lit-shoelace-esbuild-template) - + Wails template providing frontend with lit, Shoelace component library + + pre-configured prettier and typescript. diff --git a/docs/src/content/docs/contributing/_category_.json b/docs/src/content/docs/contributing/_category_.json new file mode 100644 index 000000000..5f76b677d --- /dev/null +++ b/docs/src/content/docs/contributing/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Technical Documentation", + "link": { + "type": "generated-index", + "description": "Deep dive into Wails v3 internals for developers who want to understand or contribute to the codebase." + }, + "order": 50, + "collapsed": false +} diff --git a/docs/src/content/docs/contributing/architecture.mdx b/docs/src/content/docs/contributing/architecture.mdx new file mode 100644 index 000000000..6490a35bc --- /dev/null +++ b/docs/src/content/docs/contributing/architecture.mdx @@ -0,0 +1,165 @@ +--- +title: Wails v3 Architecture +description: Deep-dive diagrams and explanations of every moving part inside Wails v3 +sidebar: + order: 1 +--- + +import Mermaid from "../../components/Mermaid.astro"; + +Wails v3 is a **full-stack desktop framework** consisting of a Go runtime, +a JavaScript bridge, a task-driven tool-chain and a collection of templates that +let you ship native applications powered by modern web tech. + +This page presents the *big picture* in four diagrams: + +1. **Overall Architecture** – how every subsystem connects +2. **Runtime Flow** – what happens when JS calls Go and vice-versa +3. **Development vs Production** – two modes of the asset server +4. **Platform Implementations** – where OS-specific code lives + +--- + +## 1 · Overall Architecture + + +flowchart TD + subgraph Developer + CLI[wails3 CLI] + end + subgraph Build["Build-time Tool-chain"] + GEN["Binding Generator\n(static analysis)"] + TMP["Template Engine"] + ASSETDEV["Asset Server (dev)"] + PKG["Cross-compilation & Packaging"] + end + subgraph Runtime["Native Runtime"] + RT["Desktop Runtime\n(window, dialogs, tray, …)"] + BRIDGE["Message Bridge\n(JSON channel)"] + end + subgraph App["Your Application"] + BACKEND["Go Backend"] + FRONTEND["Web Frontend\n(React/Vue/…)"] + end + + CLI -->|init| TMP + CLI -->|generate| GEN + CLI -->|dev| ASSETDEV + CLI -->|build| PKG + + GEN -->|Go & TS stubs| BACKEND + GEN -->|bindings.json| FRONTEND + + ASSETDEV <-->|HTTP| FRONTEND + + BACKEND <--> BRIDGE <--> FRONTEND + BRIDGE <--> RT + RT <-->|serve assets| ASSETDEV + + +--- + +## 2 · Runtime Call Flow + + +sequenceDiagram + participant JS as JavaScript (frontend) + participant Bridge as Bridge (WebView callback) + participant MP as Message Processor (Go) + participant Go as Bound Go Function + + JS->>Bridge: invoke("Greet","Alice") + Bridge->>MP: JSON {t:c,id:42,...} + MP->>Go: call Greet("Alice") + Go-->>MP: "Hello Alice" + MP-->>Bridge: JSON {t:r,id:42,result:"Hello Alice"} + Bridge-->>JS: Promise.resolve("Hello Alice") + + +Key points: + +* **No HTTP / IPC** – the bridge uses the native WebView’s in-memory channel +* **Method IDs** – deterministic FNV-hash enables O(1) lookup in Go +* **Promises** – errors propagate as rejections with stack & code + +--- + +## 3 · Development vs Production Asset Flow + + +flowchart LR + subgraph Dev["`wails3 dev`"] + VITE["Framework Dev Server\n(port 5173)"] + ASDEV["Asset Server (dev)\n(proxy + disk)"] + FRONTENDDEV[Browser] + end + subgraph Prod["`wails3 build`"] + EMBED["Embedded FS\n(go:embed)"] + ASPROD["Asset Server (prod)\n(read-only)"] + FRONTENDPROD[WebView Window] + end + + VITE <-->|proxy / HMR| ASDEV + ASDEV <-->|http| FRONTENDDEV + + EMBED --> ASPROD + ASPROD <-->|in-memory| FRONTENDPROD + + +* In **dev** the server proxies unknown paths to the framework’s live-reload + server and serves static assets from disk. +* In **prod** the same API is backed by `go:embed`, producing a zero-dependency + binary. + +--- + +## 4 · Platform-Specific Runtime Split + + +classDiagram + class runtime::Window { + +Show() + +Hide() + +Center() + } + + runtime::Window <|-- Window_darwin + runtime::Window <|-- Window_linux + runtime::Window <|-- Window_windows + + class Window_darwin { + //go:build darwin + +NSWindow* ptr + } + class Window_linux { + //go:build linux + +GtkWindow* ptr + } + class Window_windows { + //go:build windows + +HWND ptr + } + + note for runtime::Window "Shared interface\nin pkg/application" + note for Window_darwin "Objective-C (Cgo)" + note for Window_linux "Pure Go GTK calls" + note for Window_windows "Win32 API via syscall" + + +Every feature follows this pattern: + +1. **Common interface** in `pkg/application` +2. **Message processor** entry in `pkg/application/messageprocessor_*.go` +3. **Implementation** per OS under `internal/runtime/*.go` guarded by build tags + +Missing functionality on an OS should return `ErrCapability` and register +availability via `internal/capabilities`. + +--- + +## Summary + +These diagrams outline **where the code lives**, **how data moves**, and +**which layers own which responsibilities**. +Keep them handy while exploring the detailed pages that follow – they are your +map to the Wails v3 source tree. diff --git a/docs/src/content/docs/contributing/asset-server.mdx b/docs/src/content/docs/contributing/asset-server.mdx new file mode 100644 index 000000000..e49cd6700 --- /dev/null +++ b/docs/src/content/docs/contributing/asset-server.mdx @@ -0,0 +1,203 @@ +--- +title: Asset Server +description: How Wails v3 serves and embeds your web assets in development and production +sidebar: + order: 4 +--- + +## Overview + +Every Wails application ships a **single native executable** that combines: + +1. Your *Go* backend +2. A *Web* frontend (HTML + JS + CSS) + +The **Asset Server** is the glue that makes this possible. +It has **two operating modes** that are selected at compile-time via Go build +tags: + +| Mode | Tag | Purpose | +|------|-----|---------| +| **Development** | `//go:build dev` | Fast iteration with hot-reload | +| **Production** | `//go:build !dev` | Zero-dependency, embedded assets | + +The implementation lives in +`v3/internal/assetserver/` with clear file splits: + +``` +assetserver_dev.go # ⬅️ runtime dev server +assetserver_production.go # ⬅️ embedded server +assetserver_darwin.go # OS-specific helpers (same for linux/windows) +asset_fileserver.go # Shared static file logic +content_type_sniffer.go # MIME type detection +ringqueue.go # Tiny LRU for mime cache +``` + +--- + +## Development Mode + +### Lifecycle + +1. `wails3 dev` boots and **spawns your frontend dev server** + (Vite, SvelteKit, React-SWC …) by running the task defined in + `frontend/Taskfile.yml` (usually `npm run dev`). +2. Wails starts the **Dev Asset Server** listening on `localhost:` and + tells the Go runtime to load `http://:` as the window URL. +3. Incoming requests are handled by `assetserver_dev.go`: + + ``` + ┌─────────┐ /runtime/... ┌─────────────┐ + │ Browser │ ── native bridge ───▶ │ Runtime │ + ├─────────┤ └─────────────┘ + │ JS │ / (index.html) proxy / -> Vite + └─────────┘ ◀─────────────┐ + AssetServer │ + ▼ + ┌────────────┐ + │ Vite Dev │ + │ Server │ + └────────────┘ + ``` + +4. Static files (`/assets/logo.svg`) are **served directly from disk** via + `asset_fileserver.go` (for speed) while anything unknown is **proxied** to + the framework dev server, giving you *instant* hot-module replacement. + +### Features + +* **Live Reload** – Vite/Snowpack/… injects HMR WebSocket; Wails only has to + proxy it. +* **Source Map Support** – because assets are not bundled, your browser devtools + map errors back to original source. +* **No Go Re-compile** – Only the frontend rebuilds; Go code stays running until + you change `.go` files. + +### Switching Frameworks + +The dev proxy is **framework-agnostic**: + +* The `wails.json` template injects two env vars: + `FRONTEND_DEV_HOST` & `FRONTEND_DEV_PORT`. +* Taskfiles for each template emit those vars before running their dev servers. +* `assetserver_dev.go` simply proxies to that target. + +Add a new template → define its dev task → Asset Server just works. + +--- + +## Production Mode + +When you run `wails3 build` the pipeline: + +1. Runs the frontend **production build** (`npm run build`) producing + `/frontend/dist/**`. +2. **Embeds** that folder into `go:embed` FS at compile time (see + `bundled_assetserver.go` generated file). +3. Compiles the Go binary with `-tags production` (implicit). + +### Request Handling + +```go +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // 1. Try embedded static assets (exact path) + // 2. Fallback to index.html for SPA routing + // 3. Sniff content-type if extension unknown + // 4. Set strong cache headers +} +``` + +* **MIME Detection** – If the build tool produced extension-less files (e.g. + `/assets/manifest`) `content_type_sniffer.go` inspects the first 512 bytes and + caches the result in a tiny lock-free LRU. +* **Ring Queue Caching** – Frequently accessed assets (logo, CSS) are kept + in-memory for the lifetime of the app, removing the need for disk or embed FS + lookups. +* **Security Headers** – Disallows `file://` navigation, enables `nosniff`. + +Because everything is embedded, the shipped binary has **no external +dependencies** (even on Windows). + +--- + +## Bridging Dev ↔ Prod + +Both modes expose the **same public interface**: + +```go +type AssetServer interface { + URL() string // dev: http://localhost:34115, prod: wails://app + Open() error // start listening + Close() // graceful shutdown +} +``` + +`pkg/application` happily uses whichever implementation was compiled in, meaning +**your application code does not change** between `dev` and `build`. + +--- + +## How Frontend Frameworks Integrate + +### Templates + +Each official template (React, Vue, Svelte, Solid…) contains: + +* `frontend/Taskfile.yml` +* `frontend/vite.config.ts` (or equivalent) + +They export two tasks: + +| Task | Purpose | +|------|---------| +| `dev` | Starts the framework dev server on a **random free port** and prints it to stdout (`PORT=5173`). | +| `build` | Produces static assets into `dist/` + manifest for cache-busting. | + +`internal/commands/dev.go` parses that stdout, sets `FRONTEND_DEV_*` env vars +and launches the **Dev Asset Server**. + +Frameworks remain fully decoupled from Go: + +* No need to import Wails JS SDK at build time – the runtime injects it at + window creation. +* Any framework with an HTTP dev server can plug in. + +--- + +## Extending / Customising + +Need custom headers, auth, or gzip? + +1. Implement `type Middleware func(http.Handler) http.Handler` +2. Register via `internal/assetserver/options.go` +3. For prod, remember to add the same middleware in `assetserver_production.go`. + +--- + +## Key Source Files + +| File | Role | +|------|------| +| `assetserver_dev.go` | Reverse proxy + disk file server | +| `assetserver_production.go` | Embedded FS handler | +| `options.go` | Config struct parsed from `pkg/options/assetserver` | +| `build_dev.go` / `build_production.go` | Build-tag wrappers selecting correct implementation | +| `bundled_assetserver.go` | Generated embed data (only present in production builds) | + +--- + +## Gotchas & Debugging + +* **White Screen in Prod** – usually SPA routing: ensure `History API Fallback` + is enabled in dev and `index.html` fallback works in prod. +* **404 in Dev** – mis-matched `FRONTEND_DEV_PORT`; run with + `WAILSDEV_VERBOSE=1` to print every proxied request. +* **Large Assets** – they are embedded; consider + [`assetserver.WithExternalDir("/path")`](https://pkg.go.dev) to load from disk. + +--- + +You now know how the Wails **Asset Server** feeds your web code to the native +window in both **development** and **production**. +Master this layer and you can debug loading issues, add middlewares, or even +swap in a completely different frontend tool-chain with confidence. diff --git a/docs/src/content/docs/contributing/binding-system.mdx b/docs/src/content/docs/contributing/binding-system.mdx new file mode 100644 index 000000000..071dc82f1 --- /dev/null +++ b/docs/src/content/docs/contributing/binding-system.mdx @@ -0,0 +1,240 @@ +--- +title: Binding System +description: How Wails v3 lets Go and JavaScript call each other with zero boilerplate +sidebar: + order: 5 +--- + +> “Bindings” are the **type-safe contract** that lets you write: + +```go +msg, err := chatService.Send("Hello") +``` + +in Go *and* + +```ts +const msg = await window.backend.ChatService.Send("Hello") +``` + +in TypeScript **without manual glue code**. +This document breaks down *how* that magic happens, from **static analysis** at +build time, through **code generation**, to the **runtime bridge** that moves +bytes over the WebView. + +--- + +## 1. 30-Second Overview + +| Stage | Component | Output | +|-------|-----------|--------| +| **Analysis** | `internal/generator/analyse.go` | In-memory AST of exported Go structs, methods, params, return types | +| **Generation** | `internal/generator/render/*.tmpl` | • `pkg/application/bindings.go` (Go)
• `frontend/src/wailsjs/**.ts` (TS defs)
• `runtime/desktop/@wailsio/runtime/internal/bindings.json` | +| **Runtime** | `pkg/application/messageprocessor_call.go` + JS runtime (`invoke.ts`) | JSON messages over WebView native bridge | + +The flow is orchestrated by the `wails3` CLI: + +``` +wails3 generate ─┬─> generator.Collect() // parse Go packages + ├─> generator.Analyse() // build semantic model + └─> generator.Render() // write files + `go fmt` +``` + +--- + +## 2. Static Analysis + +### Entry Point + +``` +internal/generator/analyse.go +``` + +`Analyse(cfg generator.Config)`: + +1. Recursively loads provided Go packages with `go/packages`. +2. Walks the *type* and *value* specs of every AST file. +3. Filters **exported** symbols (capitalised) outside `internal/` packages. +4. Records: + * Receiver type (`struct`, `interface`) + * Method name + * Parameter list (name, type, pointer, variadic) + * Return tuple (values + error presence) +5. Normalises types to a **serialisable set** + (`int`, `float64`, `string`, `[]byte`, slices, maps, structs, pointers). + +Unsupported types produce a **compile-time error** so mistakes are caught early. + +### Model + +```go +type Method struct { + ID uint32 // stable hash for runtime lookup + Name string + Params []Param + Results []Result + Receiver *Struct // nil for package funcs +} +``` + +A deterministic **FNV-1a** hash (`internal/hash/fnv.go`) of +`.()` becomes the *method ID* used at runtime. + +--- + +## 3. Code Generation + +### Templates + +`internal/generator/render/` contains text/template files: + +| Template | Target | +|----------|--------| +| `go_bindings.tmpl` | `pkg/application/bindings.go` | +| `ts_bindings.tmpl` | `frontend/wailsjs/go/models.d.ts` | +| `ts_index.tmpl` | `frontend/wailsjs/index.ts` | + +Add or adjust templates here to customise generation. + +### Go Output + +`bindings.go` registers a lookup table: + +```go +var bindings = []application.BoundMethod{ + {ID: 0x7A1201D3, Name: "ChatService.Send", Call: chatServiceSend}, +} + +func chatServiceSend(ctx context.Context, in []byte) ([]byte, error) { + var req struct{ Msg string } + if err := json.Unmarshal(in, &req); err != nil { return nil, err } + res, err := chatService.Send(req.Msg) + return json.Marshal(res), err +} +``` + +Key points: + +* **Zero reflection** at runtime → performance. +* Marshal/Unmarshal is **per-method** with generated struct wrappers. + +### TypeScript Output + +```ts +export namespace backend { + export namespace ChatService { + function Send(msg: string): Promise; + } +} +``` + +* Emitted as **ES modules** so any bundler can tree-shake. +* Method IDs are embedded in an auto-generated `bindings.json` for the JS + runtime. + +--- + +## 4. Runtime Invocation Protocol + +### JavaScript Side + +```ts +import { invoke } from "@wailsio/runtime"; + +await invoke(0x7a1201d3 /* ChatService.Send */, ["Hello"]); +``` + +Implementation: `runtime/desktop/@wailsio/runtime/invoke.ts` + +1. Packs `{t:"c", id:, p:[...args]}` into JSON. +2. Calls `window.external.invoke(payload)` (WebView2) or equivalent. +3. Returns a `Promise` that resolves/rejects based on the reply. + +### Go Side + +1. `messageprocessor_call.go` receives the JSON. +2. Looks up `methodID` in the `bindings` slice. +3. Executes the generated stub (`chatServiceSend`). +4. Serialises `{result, error}` back to JS. + +### Error Mapping + +| Go | JavaScript | +|----|------------| +| `error == nil` | `Promise` resolves with result | +| `errors.New(...)` | `Promise` rejects with `{message, stack, code}` | + +The mapping code lives in `runtime/desktop/@wailsio/runtime/errors.ts`. + +--- + +## 5. Calling JavaScript from Go + +*Browser → Go* is covered above. +*Go → Browser* uses **Events** or **Eval**: + +```go +window.Eval(`alert("Hi")`) +app.Publish("chat:new-message", msg) +``` + +Binding generation is one-way (Go methods). +For JS-exposed functions use `runtime.EventsOn` in JS and `application.Publish` +from Go. + +--- + +## 6. Extending & Troubleshooting + +### Adding Custom Serialisers + +* Implement `generator.TypeConverter` interface. +* Register in `generator.Config.Converters`. +* Update JS runtime deserialisation if needed. + +### Unsupported Type Error + +``` +error: field "Client" uses unsupported type: chan struct{} +``` + +→ wrap the channel in a struct with an exposed API or redesign. + +### Version Skew + +Bindings are regenerated on **every** `wails3 dev` / `wails3 build`. +If IDE intellisense shows stale stubs, delete `frontend/wailsjs` and rebuild. + +### Performance Tips + +* Prefer **value** receivers for small structs to reduce allocations. +* Avoid large byte slices over the bridge; use `application.FileServer` instead. +* Batch multiple quick calls into one method to minimise bridge latency. + +--- + +## 7. Key Files Map + +| Concern | File | +|---------|------| +| Static analysis entry | `internal/generator/analyse.go` | +| Render pipeline | `internal/generator/generate.go` | +| Template assets | `internal/generator/render/*.tmpl` | +| Go binding table | `pkg/application/bindings.go` (generated) | +| Call processor | `pkg/application/messageprocessor_call.go` | +| JS runtime | `runtime/desktop/@wailsio/runtime/invoke.ts` | +| Errors mapping | `runtime/desktop/@wailsio/runtime/errors.ts` | + +Keep this cheat-sheet handy when you trace a bridge bug. + +--- + +## 8. Recap + +1. **Generator** scans your Go code → semantic model. +2. **Templates** emit **Go stubs** + **TypeScript definitions**. +3. **Message Processor** executes stubs at runtime. +4. **JS Runtime** wraps it all in idiomatic promises. + +All without reflection, without IPC servers, and without you writing a single +line of boilerplate. That’s the Wails v3 binding system. Go forth and bind! diff --git a/docs/src/content/docs/contributing/build-packaging.mdx b/docs/src/content/docs/contributing/build-packaging.mdx new file mode 100644 index 000000000..78878200e --- /dev/null +++ b/docs/src/content/docs/contributing/build-packaging.mdx @@ -0,0 +1,211 @@ +--- +title: Build & Packaging Pipeline +description: What happens under the hood when you run `wails3 build`, how cross-platform binaries are produced, and how installers are generated for each OS. +sidebar: + order: 6 +--- + +`wails3 build` is a **single command** that drives a _multi-stage_ pipeline: + +1. **Frontend production build** (Vite / React / …) +2. **Asset embedding** into Go sources +3. **Native compilation** for the target OS/arch +4. **Post-processing** (icon injection, version info, codesign) +5. **Packaging** into a distributable artefact (AppImage, DMG, MSI, …) + +This document follows every stage, shows where the code lives, and explains how +to customise or debug it. + +--- + +## 1. Entry Point: `internal/commands/build-assets.go` + +``` +wails3 build # cmd/wails3/main.go +└─ internal/commands.Build() # build-assets.go +``` + +High-level tasks are delegated to **Taskfile** targets so the same logic runs in +CI or locally. + +| Stage | Taskfile target | Go implementation | +|-------|-----------------|-------------------| +| Frontend build | `frontend:build` | `internal/commands/task.go` | +| Embed assets | `embed:assets` | generated `bundled_assetserver.go` | +| Go compile | `build:go` | `tool_buildinfo.go`, `tool_package.go` | +| Package | `package:*` | `internal/packager`, `internal/commands/*` | + +--- + +## 2. Frontend Production Build + +1. The CLI changes into `frontend/` and executes the `build` task found in the + project `Taskfile.yml` (`npm run build` by default). +2. Output is written to `frontend/dist/`. +3. A **content hash** manifest is produced (`manifest.json`) so cache-busting + works out of the box. + +If the task fails the pipeline aborts early, returning the exit code of your +build tool. + +--- + +## 3. Embedding Assets + +Implemented in `internal/assetserver/build_production.go`: + +* Walks `frontend/dist` and generates a `go:embed` file + `bundled_assetserver.go` (ignored by git). +* Adds the `production` build tag. + +Result: the final binary contains every HTML/JS/CSS byte, so the executable is +self-contained. + +--- + +## 4. Native Compilation + +### Build Flags + +| Flag | Purpose | +|------|---------| +| `-tags production` | Select prod asset server & runtime stubs | +| `-ldflags "-s -w"` | Strip symbol table & DWARF (smaller binaries) | +| `-X internal/buildinfo.BuildTime=$(date)` | Embed reproducible metadata | + +`internal/commands/tool_buildinfo.go` collects version, git commit, and build +time then injects them using `go build -ldflags`. + +### Cross Compilation Matrix + +| OS | Arch | Build Tag | CGO | Notes | +|----|------|-----------|-----|-------| +| Windows | amd64 / arm64 | `windows` | cgo enabled for WebView2 | Generates `.syso` via `internal/commands/syso.go` | +| macOS | amd64 / arm64 | `darwin` | cgo enabled (Objective-C) | Codesign & notarisation tasks available | +| Linux | amd64 / arm64 | `linux` | pure Go (webkit option) | GTK/WebKitGTK headers required for CGO build | + +`wails3 build -platform darwin/arm64` overrides GOOS/GOARCH. +If CGO is needed on the host that can't compile the target (e.g. building +Windows from Linux), the CLI falls back to **Docker** images (`ghcr.io/wailsapp/cross-compiler`). + +--- + +## 5. Post-Processing + +### Icons & Resources + +* **Windows** – `syso.go` merges your PNG/ICO into a `.syso` that `go build` + links automatically. Also writes `manifest.xml` enabling High-DPI support. +* **macOS** – `icons.go` turns `icon.png` → `.icns`, injects into + `MyApp.app/Contents/Resources`. +* **Linux** – `.desktop` files are generated in `/usr/share/applications` + by each packager backend. + +### Code Signing (Optional) + +* macOS: `codesign` + `xcrun altool --notarize-app` +* Windows: `signtool.exe` +* Linux: Not common (repository GPG handled externally) + +Task targets: `sign:mac`, `sign:windows`. + +--- + +## 6. Packaging Back-Ends + +### Linux + +| Artefact | Code Path | Tooling | +|----------|-----------|---------| +| **AppImage** (default) | `internal/commands/appimage.go` | `linuxdeploy` + `linuxdeploy-plugin-gtk` | +| **deb / rpm / pacman** | `internal/packager` | `fpm` (invoked via Go) | + +Flags: + +``` +wails3 build -package deb # produces myapp_1.0.0_amd64.deb +wails3 build -package rpm +``` + +### macOS + +* **DMG** – `internal/commands/packager.go` calls `appdmg` to generate a + drag-&-drop installer. +* **ZIP** – fallback if `appdmg` is missing. +* Sets CFBundle identifiers and version plist automatically. + +### Windows + +* **MSI** – `internal/commands/packager.go` wraps **WiX** candle & light tools. + Heat autogenerates the component tree from the built `.exe`. + +Extra resources: + +* `internal/commands/windows_resources/` contains templated **.wxs** fragments. +* Version info injected via `rsrc` utility. + +--- + +## 7. Build Artefact Layout + +After a successful build the CLI prints: + +``` +build/bin/ +├── myapp-linux-amd64 +└── myapp-linux-amd64.AppImage + +build/package/ +└── myapp_1.0.0_amd64.deb +``` + +The exact files depend on `-platform` and `-package` flags. + +--- + +## 8. Customising the Pipeline + +| Need | Approach | +|------|----------| +| Run a linter before build | Add a `lint` task in **Taskfile.yml** and make `build` depend on it. | +| Extra linker flags | `wails3 build -ldflags "-H windowsgui"` | +| Skip packaging | `wails3 build -skip-package` (keeps raw binary). | +| Bring your own packager | Implement `internal/packager/.go`, register in `packager.go`. | + +All Taskfile targets use environment variables exported by the CLI, so you can +reference values like `$WAILS_VERSION` or `$WAILS_PLATFORM` inside custom tasks. + +--- + +## 9. Troubleshooting + +| Symptom | Likely Cause | Fix | +|---------|--------------|-----| +| **`ld: framework not found WebKit` (mac)** | Xcode CLI tools missing | `xcode-select --install` | +| **Blank window in prod build** | Frontend build failed or SPA routing | Check `frontend/dist/index.html` exists and `History API Fallback` is set. | +| **`wixl` not found** (Linux MSI cross-build) | WiX toolset not installed in Docker image | Use `--docker` build or install WiX manually. | +| **Duplicated symbol `_WinMain`** | Mixed `windowsgui` flag and syso | Remove custom `-ldflags` or let Wails manage resources. | + +Verbose mode: `wails3 build -verbose` dumps every command executed. + +--- + +## 10. Key Source Map + +| Concern | File | +|---------|------| +| Task runner glue | `internal/commands/task_wrapper.go` | +| Build dispatcher | `internal/commands/build-assets.go` | +| AppImage builder | `internal/commands/appimage.go` | +| Generic packager interface | `internal/packager/packager.go` | +| Windows resource generator | `internal/commands/syso.go` | +| Build info injector | `internal/commands/tool_buildinfo.go` | +| Version constants | `internal/version/version.go` | + +Keep this table handy when you trace a build failure. + +--- + +You now have the full picture from **source code** to **installer**. Armed with +this knowledge you can tweak the pipeline, add new packaging targets, or debug +cross-compilation issues without fear. Happy shipping! diff --git a/docs/src/content/docs/contributing/codebase-layout.mdx b/docs/src/content/docs/contributing/codebase-layout.mdx new file mode 100644 index 000000000..955d66d02 --- /dev/null +++ b/docs/src/content/docs/contributing/codebase-layout.mdx @@ -0,0 +1,210 @@ +--- +title: Codebase Layout +description: How the Wails v3 repository is organised and how the pieces fit together +sidebar: + order: 2 +--- + +Wails v3 lives in a **monorepo** that contains the framework runtime, CLI, +examples, documentation, and build tool-chain. +This page walks through the *directory structure* that matters to anyone digging +into the internals. + +## Top-Level View + +``` +wails/ +├── v3/ # ⬅️ Everything specific to Wails v3 lives here +├── v2/ # Legacy v2 implementation (can be ignored for v3 work) +├── docs/ # Astro-powered v3 docs site (this page!) +├── website/ # Docusaurus v2 site and marketing pages (main site) +├── scripts/ # Misc helper scripts (e.g. sponsor image generator) +├── mkdocs-website/ # Deprecated v3 docs prototype +└── *.md # Project-wide meta files (CHANGELOG, LICENSE, …) +``` + +From here on, we zoom into the **`v3/`** tree. + +## `v3/` Root + +``` +v3/ +├── cmd/ # Compilable commands (currently only wails3 CLI) +├── examples/ # Hands-on sample apps testing every feature +├── internal/ # Framework implementation (not public API) +├── pkg/ # Public Go packages ‑ the API surface +├── tasks/ # Taskfile-based release utilities +├── templates/ # RFC-style proposals (WEP) + common assets +├── go.mod +└── go.sum +``` + +### Mental Model + +1. **`pkg/`** exposes *what application developers import* +2. **`internal/`** contains *how the magic is implemented* +3. **`cmd/wails3`** drives *project lifecycle & builds* +4. **`examples/`** double as *living tests* and *reference code* + +Everything else supports those three pillars. + +--- + +## `cmd/` – Commands + +| Path | Notes | +|------|-------| +| `v3/cmd/wails3` | The **CLI entrypoint**. A tiny `main.go` delegates all logic to packages in `internal/commands`. | +| `internal/commands/*` | Sub-commands (init, dev, build, doctor, …). Each lives in its own file for easy discoverability. | +| `internal/commands/task_wrapper.go` | Bridges between CLI flags and the Taskfile build pipeline. | + +The CLI owns: + +* **Project scaffolding** (`init`, template generation) +* **Dev server orchestration** (`dev`, live-reload) +* **Production builds & packaging** (`build`, `package`, platform wrappers) +* **Diagnostics** (`doctor`) + +--- + +## `internal/` – The Engine Room + +``` +internal/ +├── assetserver/ # Serving & embedding web assets +├── buildinfo/ # Reproducible build metadata +├── commands/ # CLI mechanics (see above) +├── runtime/ # Desktop runtime (per-platform) +├── generator/ # Static analysis & binding generator +├── templates/ # Project templates (frontend stacks) +├── packager/ # Linux AppImage/deb/rpm, Windows MSI, macOS DMG +├── capabilities/ # Host OS capability probing +├── dbus/ # Linux system tray integration +├── service/ # IPC micro-services framework +└── ... # [other helper sub-packages] +``` + +### Key Sub-Packages + +| Package | Responsibility | Where It Connects | +|---------|----------------|-------------------| +| `runtime` | Window/event loop, clipboard, dialogs, system tray. One file per OS with build-tags (`*_darwin.go`, `*_linux.go`, …). | Called by `pkg/application` and message processor. | +| `assetserver` | Dual-mode file server:
• Dev: serves from disk & proxies Vite
• Prod: embeds assets via `go:embed` | Initialized by `pkg/application` during startup. | +| `generator` | Parses Go source to build **binding metadata** which later produces TypeScript stub files and Go marshal/unmarshal code. | Triggered by `wails3 init` / `wails3 generate`. | +| `packager` | Wraps platform-specific packaging CLIs (eg. `electron-builder` equivalents) into Go for cross-platform automation. | Invoked by `wails3 package`. | + +Supporting utilities (eg. `s/`, `hash/`, `flags/`) keep internal concerns decoupled. + +--- + +## `pkg/` – Public API + +``` +pkg/ +├── application/ # Core API: windows, menus, dialogs, events, … +├── runtime/ # JS-side helpers (bridge, events, screen, ...) +├── options/ # Strongly-typed configuration structs +├── menu/ # Declarative menu DSL +└── ... # Platform helpers (mac/, windows/, assetserver/, …) +``` + +`pkg/application` bootstraps a Wails program: + +```go +func main() { + app := application.New(application.Options{ + Name: "MyApp", + Assets: application.NewAssetsFS(assetsFS), + }) + window := app.Window.New(&application.WebviewWindowOptions{ + Title: "Hello", + Width: 1024, + Height: 768, + }) + app.Run() +} +``` + +Under the hood it: + +1. Spins up `internal.runtime.*` +2. Sets up an `assetserver` +3. Registers bindings generated by `internal.generator` +4. Enters the OS main thread + +--- + +## `examples/` – Living Specs + +Each sub-folder is a **self-contained application** exercising one feature. +Patterns you’ll see mirrored in real code: + +* Binding services (`examples/binding/`) +* Multi-window (`examples/window/`) +* System tray (`examples/systray-*`) +* Packaging (`examples/file-association/`) + +When in doubt, start here and drill into implementation. + +--- + +## `templates/` – Scaffolding Blueprints + +`internal/templates` ships **base templates** (Go layout) and **frontend skins** +(React, Svelte, Vue, …). +At `wails3 init -t react`, the CLI: + +1. Copies `_common` Go files +2. Merges desired frontend pack +3. Runs `npm install` + `task deps` + +Editing templates **does not** affect existing apps, only future `init`s. + +--- + +## `tasks/` – Release Automation + +Taskfiles wrap complex cross-compilation, version bumping, and changelog +generation. They are consumed programmatically by `internal/commands/task.go` +so the same logic powers **CLI** and **CI**. + +--- + +## How the Pieces Interact + +```mermaid +flowchart TD + A[wails3 CLI] -- build/generate --> B[internal.generator] + A -- dev --> C[assetserver (dev)] + A -- package --> P[internal.packager] + + subgraph App runtime + E[pkg.application] --> F[internal.runtime] + F --> G[OS APIs] + E --> C + end + + B --> E %% generated bindings registered at init +``` + +*CLI → generator → runtime* forms the core path from **source** to **running +desktop app**. + +--- + +## Orientation Tips + +| Need to understand… | Look at… | +|---------------------|----------| +| Platform shims | `internal/runtime/*_DARWIN.go`, `*_windows.go`, … | +| Bridge protocol | `pkg/application/messageprocessor_*.go` | +| Asset workflow | `internal/assetserver`, `v3/templates/base/assets` | +| Packaging flow | `internal/commands/appimage.go`, `internal/packager` | +| Template engine | `internal/templates/templates.go` | +| Static analysis | `internal/generator/*` | + +--- + +You now have a **mental map** of the repository. +Use it with `ripgrep`, your IDE’s “Go to File/Symbol”, and the example apps to +navigate deeper into any feature. Happy hacking! diff --git a/docs/src/content/docs/contributing/extending-wails.mdx b/docs/src/content/docs/contributing/extending-wails.mdx new file mode 100644 index 000000000..ffff66b77 --- /dev/null +++ b/docs/src/content/docs/contributing/extending-wails.mdx @@ -0,0 +1,246 @@ +--- +title: Extending Wails +description: Practical guide to adding new features and platforms to Wails v3 +sidebar: + order: 8 +--- + +> Wails is designed to be **hackable**. +> Every major subsystem lives in Go code you can read, modify, and ship. +> This page shows _where_ to start and _how_ to stay cross-platform when you: + +* Add a **Service** (file server, KV store, custom IPC…) +* Create a **new CLI command** (`wails3 `) +* Extend the **Runtime** (window API, dialogs, events) +* Introduce a **Platform capability** (Wayland, Apple Vision OS…) +* Keep **cross-platform compatibility** without drowning in `//go:build` tags + +--- + +## 1. Adding a Service + +Services are background Go components exposed to apps via the `application.Context`. + +``` +internal/service/ +├── template/ # Boilerplate for new services +│ ├── template.go +│ └── README.md +└── service.go # Registration + lifecycle interfaces +``` + +### 1.1 Define the Service + +```go +package chat + +type Service struct { + messages []string +} + +func New() *Service { return &Service{} } + +func (s *Service) Send(msg string) string { + s.messages = append(s.messages, msg) + return "ok" +} +``` + +### 1.2 Implement `service.Service` + +```go +func (s *Service) Init(ctx *application.Context) error { return nil } +func (s *Service) Shutdown() error { return nil } +``` + +### 1.3 Register Generator + +*Add to* `internal/generator/collect/services.go` + +```go +services.Register("chat", chat.New) +``` + +> Now `wails3 generate` emits bindings so JS can call +> `window.backend.chat.Send("hi")`. + +### 1.4 Ship an Example + +Copy `v3/examples/services/` and adjust. + +--- + +## 2. Writing a New CLI Command + +CLI logic lives under `internal/commands/`. +Each command is **one file** that mounts itself in `init()`. + +### 2.1 Create the File + +`v3/internal/commands/hello.go` + +```go +package commands + +import ( + "github.com/spf13/cobra" +) + +func init() { + Add(&cobra.Command{ + Use: "hello", + Short: "Prints Hello World", + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Println("Hello Wails!") + return nil + }, + }) +} +``` + +`Add()` registers with the root in `cmd/wails3/main.go`. + +### 2.2 Re-compile CLI + +``` +go install ./v3/cmd/wails3 +wails3 hello +``` + +If your command needs **Taskfile** integration, reuse helpers in +`internal/commands/task_wrapper.go`. + +--- + +## 3. Modifying the Runtime + +Common reasons: + +* New window feature (`SetOpacity`, `Shake`, …) +* Extra dialog (`ColorPicker`) +* System-level API (screen brightness) + +### 3.1 Public API + +Add to `pkg/application/window.go`: + +```go +func (w *WebviewWindow) SetOpacity(o float32) { + w.ctx.Call("window:setOpacity", o) +} +``` + +### 3.2 Message Processor + +Create `messageprocessor_window_opacity.go`: + +```go +const MsgSetOpacity = "window:setOpacity" + +func init() { register(MsgSetOpacity, handleSetOpacity) } + +func handleSetOpacity(ctx *Context, params json.RawMessage) ([]byte, error) { + var req struct { + ID uint64 `json:"id"` + Opacity float32 `json:"o"` + } + json.Unmarshal(params, &req) + return nil, runtime.SetOpacity(req.ID, req.Opacity) +} +``` + +### 3.3 Platform Implementation + +``` +internal/runtime/ + webview_window_darwin.go + webview_window_linux.go + webview_window_windows.go +``` + +Add `SetOpacity()` to each, guarded by build tags. +If a platform can’t support it, return `ErrCapability`. + +### 3.4 Capability Flag + +Expose via `internal/capabilities`. + +```go +const CapOpacity = "window:opacity" +``` + +Runtime files `*_darwin.go` etc. should `registerCapability(CapOpacity)` when +successfully initialised. + +Apps can query: + +```go +if application.HasCapability(application.CapOpacity) { ... } +``` + +--- + +## 4. Adding New Platform Capabilities + +Example: **Wayland** clipboard on Linux. + +1. Fork `internal/runtime/clipboard_linux.go`. +2. Split file: + * `clipboard_linux_x11.go` → `//go:build linux && !wayland` + * `clipboard_linux_wayland.go` → `//go:build linux && wayland` +3. Add CLI flag `--tags wayland` in `internal/commands/dev.go` + (`goEnv.BuildTags += ",wayland"`). +4. Document in **Asset Server** & README. + +> Keep default build tags minimal; reserve opt-in tags for niche features. + +--- + +## 5. Cross-Platform Compatibility Checklist + +| ✅ Step | Why | +|---------|-----| +| Provide **every** public method in all platform files (even if stub) | Keeps build green on all OS/arch | +| Gate platform specifics behind **capabilities** | Let apps degrade gracefully | +| Use **pure Go** first, CGO only when needed | Simplifies cross-compiles | +| Test with `task matrix:test` | Runs `go test ./...` on linux/mac/windows in Docker | +| Document new build tags in `docs/getting-started/installation.mdx` | Users must know flags | + +--- + +## 6. Debug Builds & Iteration Speed + +* `wails3 dev -tags debug` adds extra log hooks (`logger_dev*.go`). +* Use `task reload` to rebuild runtime without restarting the whole app. +* Race detector: `wails3 dev -race` (see `pkg/application/RACE.md`). + +--- + +## 7. Upstream Contributions + +1. Open an **issue** to discuss the idea & design. +2. Follow the techniques above to implement. +3. Add: + * Unit tests (`*_test.go`) + * Example (`v3/examples//`) + * Docs (this file or API reference) +4. Ensure `go vet`, `golangci-lint`, and CI matrix pass. + +--- + +### Quick Links + +| Area | Location | +|------|----------| +| Services framework | `internal/service/` | +| CLI core | `internal/commands/` | +| Runtime per-OS | `internal/runtime/` | +| Capability helpers| `internal/capabilities/` | +| Taskfile DSL | `tasks/Taskfile.yml` | +| Dev matrix tests | `tasks/events/generate.go` | + +--- + +You now have a **roadmap** for bending Wails to your will—add services, sprinkle +CLI magic, hack the runtime, or bring entirely new OS features. +Happy extending! diff --git a/docs/src/content/docs/contributing/index.mdx b/docs/src/content/docs/contributing/index.mdx new file mode 100644 index 000000000..a6dc96497 --- /dev/null +++ b/docs/src/content/docs/contributing/index.mdx @@ -0,0 +1,128 @@ +--- +title: Technical Overview +description: High-level architecture and roadmap to the Wails v3 codebase +sidebar: + order: 1 +--- + +import { Card, CardGrid } from "@astrojs/starlight/components"; +import Mermaid from "../../components/Mermaid.astro"; + +## Welcome to the Wails v3 Technical Documentation + +This section is **not** about community guidelines or how to open a pull-request. +Instead, it dives into **how Wails v3 is built** so that you can quickly orient +yourself in the codebase and start hacking with confidence. + +Whether you plan to patch the runtime, extend the CLI, craft new templates, or +simply understand the internals, the pages that follow provide the technical +context you need. + +--- + +## High-Level Architecture + + + + The heart of every Wails app is Go code compiled into a native executable. + It owns application logic, system integration and performance-critical + operations. + + + + UI is written with standard web tech (React, Vue, Svelte, Vanilla, …) + rendered by a lightweight system WebView (WebKit on Linux/macOS, WebView2 on + Windows). + + + + A zero-copy, in-memory bridge enables **Go⇄JavaScript** calls with automatic + type conversion, event propagation and error forwarding. + + + + `wails3` orchestrates project creation, live-reload dev server, asset + bundling, cross-compilation and packaging (deb, rpm, AppImage, msi, dmg…). + + + +--- + +## Architectural Overview + + +```mermaid +flowchart TD + subgraph Developer Environment + CLI[wails3 CLI
Init · Dev · Build · Package] + end + + subgraph Build-Time + GEN[Binding System
(Static Analysis & Codegen)] + ASSET[Asset Server
(Dev Proxy · Embed FS)] + PKG[Build & Packaging
Pipeline] + end + + subgraph Runtime + RUNTIME[Desktop Runtime
(Window · Events · Dialogs)] + BIND[Bridge
(Message Processor)] + end + + subgraph Application + GO[Go Backend
(App Logic)] + WEB[Web Frontend
(React/Vue/...)] + end + + %% Relationships + CLI --> |"generate"| GEN + CLI --> |"dev / build"| ASSET + CLI --> |"compile & package"| PKG + + GEN --> |"Code Stubs + TS"| GO + GEN --> |"Bindings JSON"| WEB + + PKG --> |"Final Binary + Installer"| GO + + GO --> |"Function Calls"| BIND + WEB --> |"Invoke / Events"| BIND + + RUNTIME <-->|native messages| BIND + RUNTIME --> |"Display Assets"| ASSET + WEB <-->|HTTP / In-Memory| ASSET +``` +
+ +The diagram shows the **end-to-end flow**: + +1. **CLI** drives generation, dev server, compilation, and packaging. +2. **Binding System** produces glue code that lets the **Web Frontend** call into the **Go Backend**. +3. During development the **Asset Server** proxies to the framework dev server; in production it serves embedded files. +4. At runtime the **Desktop Runtime** manages windows and OS APIs, while the **Bridge** shuttles messages between Go and JavaScript. + +--- + +## What This Documentation Covers + +| Topic | Why It Matters | +| ----- | -------------- | +| **Codebase Layout** | Map of `/v3` directories and how modules interact. | +| **Runtime Internals** | Window management, system APIs, message processor, and platform shims. | +| **Asset & Dev Server** | How web assets are served in dev and embedded in production. | +| **Build & Packaging Pipeline** | Taskfile-based workflow, cross-platform compilation, and installer generation. | +| **Binding System** | Static analysis pipeline that generates type-safe Go⇄TS bindings. | +| **Template System** | Generator architecture that powers `wails init -t `. | +| **Testing & CI** | Unit/integration test harness, GitHub Actions, race detector guidance. | +| **Extending Wails** | Adding services, templates, or CLI sub-commands. | + +Each subsequent page drills into these areas with concrete code samples, +diagrams, and references to the relevant source files. + +--- + +:::note +Prerequisites: You should be comfortable with **Go 1.23+**, basic TypeScript, +and modern frontend build tools. If you are new to Go, consider skimming the +official tour first. +::: + +Happy exploring — and welcome to the Wails v3 internals! diff --git a/docs/src/content/docs/contributing/runtime-internals.mdx b/docs/src/content/docs/contributing/runtime-internals.mdx new file mode 100644 index 000000000..c5a3a1398 --- /dev/null +++ b/docs/src/content/docs/contributing/runtime-internals.mdx @@ -0,0 +1,175 @@ +--- +title: Runtime Internals +description: Deep-dive into how Wails v3 boots, runs, and talks to the OS +sidebar: + order: 3 +--- + +The **runtime** is the layer that transforms ordinary Go functions into a +cross-platform desktop application. +This document explains the moving parts you will meet when tracing through the +source code. + +--- + +## 1. Application Lifecycle + +| Phase | Code Path | What Happens | +|-------|-----------|--------------| +| **Bootstrap** | `pkg/application/application.go:init()` | Registers build-time data, creates a global `application` singleton. | +| **New()** | `application.New(...)` | Validates `Options`, spins up the **AssetServer**, initialises logging. | +| **Run()** | `application.(*App).Run()` | 1. Calls platform `mainthread.X()` to enter the OS UI thread.
2. Boots the **runtime** (`internal/runtime`).
3. Blocks until the last window closes or `Quit()` is called. | +| **Shutdown** | `application.(*App).Quit()` | Broadcasts `application:shutdown` event, flushes log, tears down windows & services. | + +The lifecycle is strictly **single-entry**: you may create many windows, but the +application object itself is initialised once. + +--- + +## 2. Window Management + +### Public API + +```go +win := app.Window.New(&application.WebviewWindowOptions{ + Title: "Dashboard", + Width: 1280, + Height: 720, +}) +win.Show() +``` + +`NewWebviewWindow` delegates to `internal/runtime/webview_window_*.go` where +platform-specific constructors live: + +``` +internal/runtime/ +├── webview_window_darwin.go +├── webview_window_linux.go +└── webview_window_windows.go +``` + +Each file: + +1. Creates a native webview (WKWebView, WebKitGTK, WebView2). +2. Registers a **Message Processor** callback. +3. Maps Wails events (`WindowResized`, `Focus`, `DropFile`, …) to runtime + event IDs. + +Windows are tracked by `screenmanager.go` which offers **query APIs** (all +displays, DPI, active window) and centralises resize / move bookkeeping. + +--- + +## 3. Message Processing Pipeline + +The bridge between JavaScript and Go is implemented by the **Message +Processor** family in `pkg/application/messageprocessor_*.go`. + +Flow: + +1. **JavaScript** calls `window.runtime.Invoke("Greet", "Alice")`. +2. The runtime serialises the request: + `{"t":"c","id":"123","m":"Greet","p":["Alice"]}` + (`t` = type, `c` = call). +3. **Go** receives this JSON via the webview callback. +4. `messageprocessor_call.go` looks up the bound method in the + generated table (`application.bindings.go`) and executes it. +5. The result or error is marshalled back to JS where a `Promise` resolves. + +Specialised processors: + +| File | Purpose | +|------|---------| +| `messageprocessor_window.go` | Window actions (hide, maximize, …) | +| `messageprocessor_dialog.go` | Native dialogs (`OpenFile`, `MessageBox`, …) | +| `messageprocessor_clipboard.go` | Clipboard read/write | +| `messageprocessor_events.go` | Event subscribe / emit | +| `messageprocessor_browser.go` | Browser navigation, devtools | + +Processors are **stateless** – they pull everything they need from the +`ApplicationContext` passed with each message. + +--- + +## 4. Events System + +Events are namespaced strings dispatched across three layers: + +1. **Application events**: global lifecycle (`application:ready`, + `application:shutdown`). +2. **Window events**: per-window (`window:focus`, `window:resize`). +3. **Custom events**: user-defined (`chat:new-message`). + +Implementation details: + +* Constants are generated from `internal/events/defaults.go`. +* Go side: + `app.On("window:focus", func(e application.WindowEvent) {...})` +* JS side: + `window.runtime.EventsOn("chat:new-message", cb)` + +Under the hood both map to the same **EventBus** in +`pkg/application/events.go`. +Subscribers are reference-counted; when a window closes, its callbacks are +auto-unregistered to avoid leaks. + +--- + +## 5. Platform-Specific Implementations + +Conditional compilation keeps the public API identical while hiding OS wrinkles. + +| Concern | Darwin | Linux | Windows | +|---------|--------|-------|---------| +| Main Thread | `mainthread_darwin.go` (Cgo to Foundation) | `mainthread_linux.go` (GTK) | `mainthread_windows.go` (Win32 `AttachThreadInput`) | +| Dialogs | `dialogs_darwin.*` (NSAlert) | `dialogs_linux.go` (GtkFileChooser) | `dialogs_windows.go` (IFileOpenDialog) | +| Clipboard | `clipboard_darwin.go` | `clipboard_linux.go` | `clipboard_windows.go` | +| Tray Icons | `systemtray_darwin.*` | `systemtray_linux.go` (DBus) | `systemtray_windows.go` (Shell_NotifyIcon) | + +Key principles: + +* **No Cgo on Windows/Linux** unless unavoidable (performance, portability). +* Use **build tags** (`//go:build darwin && !production`) to keep files readable. +* Expose **capabilities** via `internal/capabilities` so higher layers can + degrade gracefully. + +--- + +## 6. File Guide + +| File | Why You’d Touch It | +|------|--------------------| +| `internal/runtime/runtime_*.go` | Change global startup logic, add debug hooks. | +| `internal/runtime/webview_window_*.go` | Implement a new window hint or behaviour. | +| `pkg/application/messageprocessor_*.go` | Add a new bridge command callable from JS. | +| `pkg/application/events_*.go` | Extend built-in event definitions. | +| `internal/assetserver/*` | Tweak dev/production asset handling. | + +--- + +## 7. Debugging Tips + +* Launch with `WAILS_LOG_LEVEL=debug` to print every message crossing the bridge. +* Use `wails3 dev -verbose` to see live reload & asset requests. +* On macOS run under `lldb --` to catch Objective-C exceptions early. +* For Windows Chromium issues, enable WebView2 debug logs: + `set WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=9222` + +--- + +## 8. Extending the Runtime + +1. Define a **capability flag** in `internal/capabilities`. +2. Implement the feature in each platform file using build tags. +3. Add public API in `pkg/application`. +4. Register a new message type or event if JS needs to call it. +5. Update at least one example in `v3/examples/` exercising the feature. + +Follow this checklist and you'll keep the cross-platform contract intact. + +--- + +You now have a guided tour of the runtime internals. Combine this knowledge with +the **Codebase Layout** map and the **Asset Server** docs to navigate confidently +and make impactful contributions. Happy coding! diff --git a/docs/src/content/docs/contributing/template-system.mdx b/docs/src/content/docs/contributing/template-system.mdx new file mode 100644 index 000000000..ee03824d0 --- /dev/null +++ b/docs/src/content/docs/contributing/template-system.mdx @@ -0,0 +1,222 @@ +--- +title: Template System +description: How Wails v3 scaffolds new projects, how templates are organised, and how to build your own. +sidebar: + order: 7 +--- + +Wails ships with a **template system** that lets `wails3 init` produce a ready-to-run +project for **any** modern frontend stack (React, Svelte, Vue, Solid, Vanilla +JS …). + +This page covers: + +1. Template directory layout +2. How the CLI chooses & renders templates +3. Creating a new template step-by-step +4. Updating or overriding existing templates +5. Troubleshooting and best practices + +--- + +## 1. Where Templates Live + +``` +v3/ +└── internal/templates/ + ├── _common/ # Files copied into EVERY project (main.go, .gitignore) + ├── base/ # Backend-only “plain Go” template + ├── react/ # React + Vite + ├── react-ts/ + ├── svelte/ + ├── vue/ + ├── ... (others) + └── templates.go # Registry + helper functions +``` + +* **`_common/`** – universal boilerplate (Go modules, Taskfile, README stub, + VS Code settings). +* **Framework folders** – contain **frontend/** and any stack-specific config. +* Folder names match the **template ID** you pass to the CLI + (`wails3 init -t react-ts`). + +> The whole directory is compiled into the CLI binary via `go:embed`, so users +> can scaffold projects offline. + +--- + +## 2. How `wails3 init` Uses Templates + +Call chain: + +``` +cmd/wails3/init.go + │ + ▼ +internal/commands/init.go ← parses flags, target dir + │ + ▼ +internal/templates.Load() ← templates.go + │ + ▼ +Template.CopyTo(dest) ← filesystem copy + text substitutions +``` + +### Substitutions + +Placeholders wrapped in `{{ }}` are replaced at copy time: + +| Placeholder | Example | Source | +|-------------|---------|--------| +| `{{ProjectName}}` | `myapp` | CLI flag/dir name | +| `{{ModulePath}}` | `github.com/me/myapp` | if `--module` provided | +| `{{WailsVersion}}`| `v3.0.0` | compiled constant | + +You can add **any** placeholder; just ensure it gets a value in +`internal/commands/init.go`. + +### Post-Copy Hooks + +Each template may ship a **Taskfile** with a `deps` task. +After copy, the CLI runs: + +``` +task --taskfile Taskfile.yml deps +``` + +Typical work: + +* `go mod tidy` +* `npm install` +* git init & initial commit (optional) + +Hook behaviour defined in `_common/Taskfile.yml` but override-able per template. + +--- + +## 3. Creating a New Template + +> Example: Add **Qwik-TS** template + +### 3.1 Folder & ID + +``` +internal/templates/qwik-ts/ +``` + +The folder name = template ID. Keep it **kebab-case**. + +### 3.2 Minimal File Set + +``` +qwik-ts/ +├── README.md # Shown in CLI with -list +├── frontend/ # Your web project +│ ├── src/ +│ ├── package.json +│ └── vite.config.ts +├── Taskfile.yml # deps & dev tasks +└── go/ # Optional extra Go files +``` + +Start by copying `react-ts` and pruning files. + +### 3.3 Update Placeholders + +Search/replace: + +* `myapp` → `{{ProjectName}}` +* `github.com/you/myapp` → `{{ModulePath}}` + +### 3.4 Register (optional) + +If the folder exists, **templates.go auto-detects it** via `embed`. +Only add explicit code if you need **custom validation**: + +```go +func (t *Template) Validate() error { + // check Node ≥ 20 for this template +} +``` + +### 3.5 Test + +```bash +wails3 init -n demo -t qwik-ts +cd demo +wails3 dev +``` + +Ensure: + +* Dev server starts +* `wailsjs` bindings generate +* Hot-reload works + +--- + +## 4. Modifying Existing Templates + +1. Edit files under `internal/templates//`. +2. Run `go generate ./v3/...` **or** just `go run ./v3/cmd/wails3 –help`; the + embed directive recompiles automatically. +3. Bump any **dependency versions** in `frontend/package.json` & `Taskfile.yml`. +4. Update `README.md` inside the template — users see it via + `wails3 init -t react --help`. + +### Common Tweaks + +| Task | Where | +|------|-------| +| Change dev server port | `frontend/Taskfile.yml` (`vite dev --port {{DevPort}}`) | +| Add environment variables | same Taskfile or `.env` | +| Replace JS package manager | Switch `npm` → `pnpm` in Taskfile | + +--- + +## 5. Template Authoring Tips + +* **Keep frontend generic** – avoid referencing Wails-specific globals; runtime + is injected automatically. +* **No compiled artefacts** – exclude `node_modules`, `dist`, `.DS_Store` via + `.tmplignore`. +* **Document prerequisites** – Node version, extra CLI tools, etc. +* **Avoid breaking changes** – create a *new* template ID if overhaul is big. + +--- + +## 6. Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `unknown template id` | Folder name mismatch | Check `wails3 init -list` | +| Placeholders not replaced | Missing entry in `Data` map | Edit `internal/commands/init.go` | +| Dev server opens blank page | Wrong `DevPort` env | Ensure Taskfile echoes port to stdout | +| Frontend fails to build in prod | Forgotten vite `base` path | Set `base: "./"` in `vite.config.ts` | + +Add `--verbose` to `wails3 init` to print every copied file and substitution. + +--- + +## 7. Key Source File Map + +| File | Responsibility | +|------|----------------| +| `internal/templates/templates.go` | Embeds template FS, returns `[]Template` | +| `internal/templates//**` | Actual template content | +| `internal/commands/init.go` | CLI glue: picks template, fills placeholders | +| `internal/commands/generate_template.go` | Utility to *export* a live project back into a template (handy for updates) | + +--- + +## 8. Recap + +* Templates live in **`internal/templates/`** and are baked into the CLI. +* `wails3 init -t ` copies `_common` + selected template, performs + substitutions, then runs a **deps** Taskfile hook. +* Creating a template is as simple as **making a folder**, adding files, and + using placeholders. +* The system is **extensible** and **self-contained** — perfect for sharing + custom stacks with your team or the community. + +Happy templating! diff --git a/docs/src/content/docs/contributing/testing-ci.mdx b/docs/src/content/docs/contributing/testing-ci.mdx new file mode 100644 index 000000000..7393e818e --- /dev/null +++ b/docs/src/content/docs/contributing/testing-ci.mdx @@ -0,0 +1,216 @@ +--- +title: Testing & Continuous Integration +description: How Wails v3 ensures quality with unit tests, integration suites, race detection, and GitHub Actions CI. +sidebar: + order: 9 +--- + +Robust desktop frameworks demand rock-solid testing. +Wails v3 employs a **layered strategy**: + +| Layer | Goal | Tooling | +|-------|------|---------| +| Unit tests | Fast feedback for isolated functions | `go test ./...` | +| Integration / Example tests | Validate whole features across packages | Example apps + headless assertions | +| Race detection | Catch data races in the runtime & bridge | `go test -race`, `wails3 dev -race` | +| CI matrix | Cross-OS confidence on every PR | GitHub Actions | + +This document explains **where tests live**, **how to run them**, and **what happens in CI**. + +--- + +## 1. Directory Conventions + +``` +v3/ +├── internal/.../_test.go # Unit tests for internal packages +├── pkg/.../_test.go # Public API tests +├── examples/... # Example apps double as integration tests +├── tasks/events/... # Generated code tests run in CI +└── pkg/application/RACE.md # Race test guidance & known safe suppressions +``` + +Guidelines: + +* **Keep unit tests next to code** (`foo.go` ↔ `foo_test.go`) +* Use the **black-box style** for `pkg/` packages (`package application_test`) +* Integration helpers go in `internal/testutil/` (shared mocks, fixtures) + +--- + +## 2. Unit Tests + +### Writing Tests + +```go +func TestColourParseHex(t *testing.T) { + c, err := colour.Parse("#FF00FF") + require.NoError(t, err) + assert.Equal(t, 255, c.R) +} +``` + +Recommendations: + +* Use [`stretchr/testify`](https://github.com/stretchr/testify) – already vendor-imported. +* Follow **table-driven** style for branches. +* Stub platform quirks behind build tags (`_test_windows.go` etc.) when needed. + +### Running Locally + +```bash +cd v3 +go test ./... -cover +``` + +Taskfile shortcut: + +``` +task test # same as above + vet + lint +``` + +--- + +## 3. Integration & Example Tests + +Every folder under `v3/examples/` is a **self-contained app**. +Three techniques turn them into tests: + +| Technique | File | What it checks | +|-----------|------|----------------| +| `go test ./...` inside example | `*_test.go` spins up `wails dev` in headless mode | Build & startup | +| CLI golden tests | `internal/commands/appimage_test.go` | Generated artefact hashes | +| Scripted e2e | `tasks/events/generate.go` | Emits Go that launches example, asserts runtime logs | + +Run all with: + +``` +task integration +``` + +> Headless mode uses **Xvfb** on Linux runners; macOS & Windows start minimized. + +--- + +## 4. Race Detection + +Data races are fatal in GUI runtimes. + +### Dedicated Doc + +See `v3/pkg/application/RACE.md` for: + +* Known benign races and suppression rationale +* How to interpret stack-traces crossing Cgo boundaries + +### Local Race Suite + +``` +go test -race ./... +``` + +or + +``` +wails3 dev -race +``` + +*The latter rebuilds runtime with race flags and launches a demo window. +Close the window; any races print after exit.* + +CI runs the race suite on **linux/amd64** (fastest) for every PR. + +--- + +## 5. GitHub Actions Workflows + +### 5.1 `build-and-test-v3.yml` + +Location: `.github/workflows/build-and-test-v3.yml` + +Key features: + +* **Matrix** over `os: [ubuntu-latest, windows-latest, macos-latest]` +* Caches Go & npm deps for speed +* Steps: + 1. `setup-go` @ 1.23 + 2. `go vet ./...` + 3. `go test ./... -v -coverprofile=cover.out` + 4. Build CLI + sample app (`wails3 build -skip-package`) + 5. Upload coverage to Codecov +* Conditional **race** job on Ubuntu: + ```yaml + - name: Race Detector + if: matrix.os == 'ubuntu-latest' + run: go test -race ./... + ``` + +### 5.2 Static Analysis + +* `semgrep.yml` – security/lint scanning (`.github/workflows/semgrep.yml`) +* `qodana.yaml` – JetBrains Qodana cloud inspection (optional in PR) + +### 5.3 Release Pipeline + +`runtime.yml` builds and publishes the JS runtime to npm on tag push; out of testing scope but referenced by build matrix. + +--- + +## 6. Local CI Parity + +Run the **exact** CI task set via Taskfile: + +``` +task ci +``` + +It executes: + +1. `lint` (golangci-lint, govet) +2. `test` (unit + race) +3. `integration` +4. `build` (for host platform) + +--- + +## 7. Troubleshooting Failing Tests + +| Symptom | Likely Cause | Fix | +|---------|--------------|-----| +| **Race in `runtime_darwin.go`** | Forgot to lock `mainthread.Call` | Use `mainthread.Sync` helper | +| **Windows headless hangs** | Console window waiting for input | Pass `-verbose` and ensure no dialogs open | +| **CI example build fails** | Template changed without bumping example deps | `task update-examples` regenerates lockfiles | +| **Coverpkg errors** | Integration test importing `main` | Switch to build tags `//go:build integration` | + +--- + +## 8. Adding New Tests + +1. **Unit** – create `*_test.go`, run `go test ./...` +2. **Integration** – update or add example app + test harness +3. **CI** – commit; the workflow auto-discovers tests + If OS-specific, add skip tags: + +```go +//go:build !windows +``` + +--- + +## 9. Key File Map + +| What | Path | +|------|------| +| Unit test example | `internal/colour/colour_test.go` | +| Integration harness | `internal/commands/appimage_test.go` | +| Race guide | `pkg/application/RACE.md` | +| Taskfile test targets | `v3/Taskfile.yaml` | +| CI workflow | `.github/workflows/build-and-test-v3.yml` | + +--- + +Quality isn’t an afterthought in Wails v3. +With unit tests, integration suites, race detection, and a cross-platform CI +matrix you can contribute confidently, knowing your changes run green on every +OS we support. +Happy testing! diff --git a/docs/src/content/docs/credits.mdx b/docs/src/content/docs/credits.mdx new file mode 100644 index 000000000..78e9f5b7d --- /dev/null +++ b/docs/src/content/docs/credits.mdx @@ -0,0 +1,62 @@ +--- +title: Credits +--- + +{/* Import the auto-generated contributors file */} + +import Contributors from "../../assets/contributors.html"; + +- [Lea Anthony](https://github.com/leaanthony) - Project owner, lead developer +- [Stffabi](https://github.com/stffabi) - Technical lead, developer and + maintainer +- [Travis McLane](https://github.com/tmclane) - Cross-compilation work, MacOS + testing +- [Atterpac](https://github.com/atterpac) - Developer, support guru, powerhouse +- [Simon Thomas](mailto:enquiries@wails.io) - Growth Hacker +- [Lyimmi](https://github.com/Lyimmi) - All things Linux +- [fbbdev](https://github.com/fbbdev) - Bindings Generator guru & core contributor + +## Sponsors +
+ + ZSA + +Sponsors +Special thanks: +JetBrains + +## Contributors + + + +## Special Mentions + +- [John Chadwick](https://github.com/jchv) - His amazing work on + [go-webview2](https://github.com/jchv/go-webview2) and + [go-winloader](https://github.com/jchv/go-winloader) have made the Windows + version possible. +- [Tad Vizbaras](https://github.com/tadvi) - His winc project was the first step + down the path to a pure Go Wails. +- [Mat Ryer](https://github.com/matryer) - For advice, support and bants. +- [Byron Chris](https://github.com/bh90210) - For his long term contributions to + this project. +- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and + feedback has been invaluable. +- [Justen Walker](https://github.com/justenwalker/) - For helping wrangle COM + issues which got v2 over the line. +- [Wang, Chi](https://github.com/patr0nus/) - The DeskGap project was a huge + influence on the direction of Wails v2. +- [Serge Zaitsev](https://github.com/zserge) - Whilst Wails does not use the + Webview project, it is still a source of inspiration. diff --git a/docs/src/content/docs/feedback.mdx b/docs/src/content/docs/feedback.mdx new file mode 100644 index 000000000..afe4a8bfb --- /dev/null +++ b/docs/src/content/docs/feedback.mdx @@ -0,0 +1,86 @@ +--- +title: v3 Alpha Feedback +sidebar: + order: 40 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +We welcome (and encourage) your feedback! Please search for existing tickets or +posts before creating new ones. Here are the different ways to provide feedback: + + + + + If you find a bug, please let us know by posting into the [v3 Alpha Feedback](https://discord.gg/bdj28QNHmT) channel on Discord. + + - The post should clearly state what the bug is and have a simple reproducible example. If the docs are unclear what *should* happen, please include that in the post. + - The post should be given the `Bug` tag. + - Please include the output of `wails doctor` in your post. + - If the bug is behavior that does not align with current documentation, e.g. a window does not resize properly, please do the following: + - Update an existing example in the `v3/example` directory or create a new example in the `v3/examples` folder that clearly shows the issue. + - Open a [PR](https://github.com/wailsapp/wails/pulls) with the title `[v3 alpha test] `. + - Please include a link to the PR in your post. + + :::caution + + _Remember_, unexpected behavior isn't necessarily a bug - it might just not do + what you expect it to do. Use `Suggestions` for this. + + ::: + + + + + + If you have a fix for a bug or an update for documentation, please do the following: + + - Open a pull request on the [Wails repository](https://github.com/wailsapp/wails). The title of the PR should start with `[v3 alpha]`. + - Create a post in the [v3 Alpha Feedback](https://discord.gg/bdj28QNHmT) channel. + - The post should be given the `PR` tag. + - Please include a link to the PR in your post. + + + + + + If you have a suggestion, please let us know by posting into the [v3 Alpha Feedback](https://discord.gg/bdj28QNHmT) channel on Discord: + + - The post should be given the `Suggestion` tag. + + Please feel free to reach out to us on [Discord](https://discord.gg/bdj28QNHmT) if you have any questions. + + + + + + - Posts can be "upvoted" by using the :thumbsup: emoji. Please apply to any posts that are a priority for you. + - Please *don't* just add comments like "+1" or "me too". + - Please feel free to comment if there is more to add to the post, such as "this bug also affect ARM builds" or "Another option would be to ....." + + + + + +There is a list of known issues & work in progress can be found +[here](https://github.com/orgs/wailsapp/projects/6). + +## Things we are looking for feedback on + +- The API + - Is it easy to use? + - Does it do what you expect? + - Is it missing anything? + - Is there anything that should be removed? + - Is it consistent between Go and JS? +- The build system + - Is it easy to use? + - Can we improve it? +- The examples + - Are they clear? + - Do they cover the basics? +- Features + - What features are missing? + - What features are not needed? +- Documentation + - What could be clearer? diff --git a/docs/src/content/docs/getting-started/installation.mdx b/docs/src/content/docs/getting-started/installation.mdx new file mode 100644 index 000000000..9c9d87885 --- /dev/null +++ b/docs/src/content/docs/getting-started/installation.mdx @@ -0,0 +1,114 @@ +--- +title: Installation +sidebar: + order: 10 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +## Supported Platforms + +- Windows 10/11 AMD64/ARM64 +- macOS 10.15+ AMD64 (Can deploy to macOS 10.13+) +- macOS 11.0+ ARM64 +- Ubuntu 24.04 AMD64/ARM64 (other Linux may work too!) + +## Dependencies + +Wails has a number of common dependencies that are required before installation: + + + + + Download Go from the [Go Downloads Page](https://go.dev/dl/). + + Ensure that you follow the official [Go installation instructions](https://go.dev/doc/install). You will also need to ensure that your `PATH` environment variable also includes the path to your `~/go/bin` directory. Restart your terminal and do the following checks: + + - Check Go is installed correctly: `go version` + - Check `~/go/bin` is in your PATH variable + - Mac / Linux: `echo $PATH | grep go/bin` + - Windows: `$env:PATH -split ';' | Where-Object { $_ -like '*\go\bin' }` + + + + + + Although Wails doesn't require npm to be installed, it is needed by most of the bundled templates. + + Download the latest node installer from the [Node Downloads Page](https://nodejs.org/en/download/). It is best to use the latest release as that is what we generally test against. + + Run `npm --version` to verify. + + :::note + If you prefer a different package manager to npm, feel free to use it. You will need to update the project Taskfiles to use it. + ::: + + + + + +## Platform Specific Dependencies + +You will also need to install platform specific dependencies: + + + + + Wails requires that the xcode command line tools are installed. This can be + done by running: + + ```sh + xcode-select --install + ``` + + + + + Wails requires that the [WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/) is installed. Almost all Windows installations will already have this installed. You can check using the `wails doctor` command. + + + + + Linux requires the standard `gcc` build tools plus `gtk3` and `webkit2gtk`. Run wails doctor after installation to be shown how to install the dependencies. If your distro/package manager is not supported, please let us know on discord. + + + + + +## Installation + +To install the Wails CLI using Go Modules, run the following commands: + +```shell +go install -v github.com/wailsapp/wails/v3/cmd/wails3@latest +``` + +If you would like to install the latest development version, run the following +commands: + +```shell +git clone https://github.com/wailsapp/wails.git +cd wails +git checkout v3-alpha +cd v3/cmd/wails3 +go install +``` + +When using the development version, all generated projects will use Go's +[replace](https://go.dev/ref/mod#go-mod-file-replace) directive to ensure +projects use the development version of Wails. + +## System Check + +Running `wails3 doctor` will check if you have the correct dependencies +installed. If not, it will advise on what is missing and help on how to rectify +any problems. + +## The `wails3` command appears to be missing? + +If your system is reporting that the `wails3` command is missing, check the +following: + +- Make sure you have followed the above `Go installation guide` correctly and + that the `go/bin` directory is in the `PATH` environment variable. +- Close/Reopen current terminals to pick up the new `PATH` variable. diff --git a/docs/src/content/docs/getting-started/your-first-app.mdx b/docs/src/content/docs/getting-started/your-first-app.mdx new file mode 100644 index 000000000..fa0978683 --- /dev/null +++ b/docs/src/content/docs/getting-started/your-first-app.mdx @@ -0,0 +1,275 @@ +--- +title: Your First Application +sidebar: + order: 20 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; +import { Badge } from '@astrojs/starlight/components'; +import { Steps } from "@astrojs/starlight/components"; +import { FileTree } from '@astrojs/starlight/components'; + +import wails_init from '../../../assets/wails_init.mp4'; +import wails_build from '../../../assets/wails_build.mp4'; +import wails_dev from '../../../assets/wails_dev.mp4'; + + +Creating your first application with Wails v3 is an exciting journey into +the world of modern desktop app development. This guide will walk you through +the process of creating a basic application, showcasing the power and simplicity +of Wails. + +
+
+ + + +1. ## Creating a New Project + + Open your terminal and run the following command to create a new Wails + project: + + ```bash + wails3 init -n myfirstapp + ``` + + This command creates a new directory called `myfirstapp` with all the + necessary files. + + + +2. ## Exploring the Project Structure + + Navigate to the `myfirstapp` directory. You'll find several files and + folders: + + + - build/ Contains files used by the build process + - appicon.png The application icon + - config.yml Build configuration + - Taskfile.yml Build tasks + - darwin/ macOS specific build files + - Info.dev.plist Development configuration + - Info.plist Production configuration + - Taskfile.yml macOS build tasks + - icons.icns macOS application icon + - linux/ Linux specific build files + - Taskfile.yml Linux build tasks + - appimage/ AppImage packaging + - build.sh AppImage build script + - nfpm/ NFPM packaging + - nfpm.yaml Package configuration + - scripts/ Build scripts + - windows/ Windows specific build files + - Taskfile.yml Windows build tasks + - icon.ico Windows application icon + - info.json Application metadata + - wails.exe.manifest Windows manifest file + - nsis/ NSIS installer files + - project.nsi NSIS project file + - wails_tools.nsh NSIS helper scripts + - frontend/ Frontend application files + - index.html Main HTML file + - main.js Main JavaScript file + - package.json NPM package configuration + - public/ Static assets + - Inter Font License.txt Font license + - .gitignore Git ignore file + - README.md Project documentation + - Taskfile.yml Project tasks + - go.mod Go module file + - go.sum Go module checksums + - greetservice.go Greeting service + - main.go Main application code + + + Take a moment to explore these files and familiarize yourself with the + structure. + + :::note + Although Wails v3 uses [Task](https://taskfile.dev/) as its + default build system, there is nothing stopping you from using `make` or any other + alternative build system. + ::: + +3. ## Building Your Application + + To build your application, execute: + + ```bash + wails3 build + ``` + + This command compiles a debug version of your application and saves it in a + new `bin` directory. + + :::note + `wails3 build` is shorthand for `wails3 task build` and will run the `build` task in `Taskfile.yml`. + ::: + + + + + Once built, you can run this like you would any normal application: + + + + + + + ```sh + ./bin/myfirstapp + ``` + + + + + + ```sh + bin\myfirstapp.exe + ``` + + + + + + ```sh + ./bin/myfirstapp + ``` + + + + + + You'll see a simple UI, the starting point for your application. As it is + the debug version, you'll also see logs in the console window. This is + useful for debugging purposes. + +4. ## Dev Mode + + We can also run the application in development mode. This mode allows you to + make changes to your frontend code and see the changes reflected in the + running application without having to rebuild the entire application. + + 1. Open a new terminal window. + 2. Run `wails3 dev`. The application will compile and run in debug mode. + 3. Open `frontend/index.html` in your editor of choice. + 4. Edit the code and change `Please enter your name below` to + `Please enter your name below!!!`. + 5. Save the file. + + This change will reflect in your application immediately. + + Any changes to backend code will trigger a rebuild: + + 1. Open `greetservice.go`. + 2. Change the line that has `return "Hello " + name + "!"` to + `return "Hello there " + name + "!"`. + 3. Save the file. + + The application will update within a matter of seconds. + + + +5. ## Packaging Your Application + + Once your application is ready for distribution, you can create + platform-specific packages: + + + + + + To create a `.app` bundle: + + ```bash + wails3 package + ``` + + This will create a production build and package it into a `.app` bundle in the `bin` directory. + + + + + To create an NSIS installer: + + ```bash + wails3 package + ``` + + This will create a production build and package it into an NSIS installer in the `bin` directory. + + + + + Wails supports multiple package formats for Linux distribution: + + ```bash + # Create all package types (AppImage, deb, rpm, and Arch Linux) + wails3 package + + # Or create specific package types + wails3 task linux:create:appimage # AppImage format + wails3 task linux:create:deb # Debian package + wails3 task linux:create:rpm # Red Hat package + wails3 task linux:create:aur # Arch Linux package + ``` + + + + + + For more detailed information about packaging options and configuration, + check out our [Packaging Guide](/guides/packaging). + +6. ## Setting up Version Control and Module Name + + Your project is created with the placeholder module name `changeme`. It's recommended to update this to match your repository URL: + + 1. Create a new repository on GitHub (or your preferred Git host) + 2. Initialize git in your project directory: + ```bash + git init + git add . + git commit -m "Initial commit" + ``` + 3. Set your remote repository (replace with your repository URL): + ```bash + git remote add origin https://github.com/username/myfirstapp.git + ``` + 4. Update your module name in `go.mod` to match your repository URL: + ```bash + go mod edit -module github.com/username/myfirstapp + ``` + 5. Push your code: + ```bash + git push -u origin main + ``` + + This ensures your Go module name aligns with Go's module naming conventions and makes it easier to share your code. + + :::tip[Pro Tip] + You can automate all of the initialisation steps by using the `-git` flag when creating your project: + ```bash + wails3 init -n myfirstapp -git github.com/username/myfirstapp + ``` + This supports various Git URL formats: + - HTTPS: `https://github.com/username/project` + - SSH: `git@github.com:username/project` or `ssh://git@github.com/username/project` + - Git protocol: `git://github.com/username/project` + - Filesystem: `file:///path/to/project.git` + ::: + + + +## Congratulations! + +You've just created, developed and packaged your first Wails application. +This is just the beginning of what you can achieve with Wails v3. + +## Next Steps + +If you are new to Wails, we recommend reading through our Tutorials next which will be a practical guide through +the various features of Wails. The first tutorial is [Creating a Service](/tutorials/01-creating-a-service). + +If you are a more advanced user, check out our [Guides](/guides) for more detailed information on how to use Wails. diff --git a/docs/src/content/docs/guides/_category_.json b/docs/src/content/docs/guides/_category_.json new file mode 100644 index 000000000..edc4bf6e3 --- /dev/null +++ b/docs/src/content/docs/guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 3, + "link": { + "type": "generated-index", + "description": "Comprehensive guides for developing Wails applications" + } +} diff --git a/docs/src/content/docs/guides/cli.mdx b/docs/src/content/docs/guides/cli.mdx new file mode 100644 index 000000000..f6c147b6c --- /dev/null +++ b/docs/src/content/docs/guides/cli.mdx @@ -0,0 +1,540 @@ +--- +title: CLI Reference +description: Complete reference for the Wails CLI commands +sidebar: +sidebar: + order: 1 +--- + +The Wails CLI provides a comprehensive set of commands to help you develop, build, and maintain your Wails applications. + +## Core Commands + +Core commands are the primary commands used for project creation, development, and building. + +All CLI commands are of the following format: `wails3 `. + +### `init` +Initializes a new Wails project. During this initialization, the `go mod tidy` command is run to bring the project packages up to date. This can be bypassed by using the `-skipgomodtidy` flag with the `init` command. + +```bash +wails3 init [flags] +``` + +#### Flags +| Flag | Description | Default | +|-----------------------|----------------------|--------------------------| +| `-p` | Package name | `main` | +| `-t` | Template name or URL | `vanilla` | +| `-n` | Project name | | +| `-d` | Project directory | `.` | +| `-q` | Suppress output | `false` | +| `-l` | List templates | `false` | +| `-git` | Git repository URL | | +| `-productname` | Product name | `My Product` | +| `-productdescription` | Product description | `My Product Description` | +| `-productversion` | Product version | `0.1.0` | +| `-productcompany` | Company name | `My Company` | +| `-productcopyright` | Copyright notice | ` now, My Company` | +| `-productcomments` | File comments | `This is a comment` | +| `-productidentifier` | Product identifier | | +| `-skipgomodtidy` | Skip go mod tidy | `false` | + +The `-git` flag accepts various Git URL formats: +- HTTPS: `https://github.com/username/project` +- SSH: `git@github.com:username/project` or `ssh://git@github.com/username/project` +- Git protocol: `git://github.com/username/project` +- Filesystem: `file:///path/to/project.git` + +When provided, this flag will: +1. Initialize a git repository in the project directory +2. Set the specified URL as the remote origin +3. Update the module name in `go.mod` to match the repository URL +4. Add all files + +### `dev` +Runs the application in development mode. This will give you a live view of your frontend code, and you can make changes and see them reflected +in the running application without having to rebuild the entire application. Changes to your Go code will also be detected and the application +will automatically rebuild and relaunch. + +```bash +wails3 dev [flags] +``` + +#### Flags +| Flag | Description | Default | +|-----------|----------------------|----------------------| +| `-config` | Config file path | `./build/config.yml` | +| `-port` | Vite dev server port | `9245` | +| `-s` | Enable HTTPS | `false` | + + + :::note + This is equivalent to running `wails3 task dev` and runs the `dev` task in the project's main Taskfile. You can customise this by editing the `Taskfile.yml` file. + ::: + + +### `build` +Builds a debug version of your application. It defaults to building for the current platform and architecture. + +```bash +wails3 build [CLI variables...] +``` + +You can pass CLI variables to customize the build: +```bash +wails3 build PLATFORM=linux CONFIG=production +``` + + :::note + This is equivalent to running `wails3 task build` which runs the `build` task in the project's main Taskfile. Any CLI variables passed to `build` are forwarded to the underlying task. You can customise the build process by editing the `Taskfile.yml` file. + ::: + +### `package` + +Creates platform-specific packages for distribution. + +```bash +wails3 package [CLI variables...] +``` + +You can pass CLI variables to customize the packaging: +```bash +wails3 package VERSION=2.0.0 OUTPUT=myapp.pkg +``` + +#### Package Types + +The following package types are available for each platform: + +| Platform | Package Type | +|----------|-------------------------------------------| +| Windows | `.exe` | +| macOS | `.app`, | +| Linux | `.AppImage`, `.deb`, `.rpm`, `.archlinux` | + + :::note + This is equivalent to `wails3 task package` which runs the `package` task in the project's main Taskfile. Any CLI variables passed to `package` are forwarded to the underlying task. You can customise the packaging process by editing the `Taskfile.yml` file. + ::: + + +### `task` +Runs tasks defined in your project's Taskfile.yml. This is an embedded version of [Taskfile](https://taskfile.dev) that allows you to define and run custom build, test, and deployment tasks. + +```bash +wails3 task [taskname] [CLI variables...] [flags] +``` + +#### CLI Variables +You can pass variables to tasks in the format `KEY=VALUE`: +```bash +wails3 task build PLATFORM=linux CONFIG=production +wails3 task deploy ENV=staging VERSION=1.2.3 +``` + +These variables can be accessed in your Taskfile.yml using Go template syntax: +```yaml +tasks: + build: + cmds: + - echo "Building for {{.PLATFORM | default "darwin"}}" + - echo "Config: {{.CONFIG | default "debug"}}" +``` + +#### Flags +| Flag | Description | Default | +|--------------|-------------------------------------------|----------| +| `-h` | Shows Task usage | `false` | +| `-i` | Creates a new Taskfile.yml | `false` | +| `-list` | Lists tasks with descriptions | `false` | +| `-list-all` | Lists all tasks (with or without descriptions) | `false` | +| `-json` | Formats task list as JSON | `false` | +| `-status` | Exits with non-zero if task is not up-to-date | `false` | +| `-f` | Forces execution even when task is up-to-date | `false` | +| `-w` | Enables watch mode for the given task | `false` | +| `-v` | Enables verbose mode | `false` | +| `-version` | Prints Task version | `false` | +| `-s` | Disables echoing | `false` | +| `-p` | Executes tasks in parallel | `false` | +| `-dry` | Compiles and prints tasks without executing | `false` | +| `-summary` | Shows summary about a task | `false` | +| `-x` | Pass-through the exit code of the task | `false` | +| `-dir` | Sets directory of execution | | +| `-taskfile` | Choose which Taskfile to run | | +| `-output` | Sets output style: [interleaved|group|prefixed] | | +| `-c` | Colored output (enabled by default) | `true` | +| `-C` | Limit number of tasks to run concurrently | | +| `-interval` | Interval to watch for changes (in seconds) | | + +#### Examples +```bash +# Run the default task +wails3 task + +# Run a specific task +wails3 task test + +# Run a task with variables +wails3 task build PLATFORM=windows ARCH=amd64 + +# List all available tasks +wails3 task --list + +# Run multiple tasks in parallel +wails3 task -p task1 task2 task3 + +# Watch for changes and re-run task +wails3 task -w dev +``` + +### `doctor` +Performs a system check and displays a status report. + +```bash +wails3 doctor +``` + +## Generate Commands + +Generate commands help create various project assets like bindings, icons, and build files. All generate commands use the base command: `wails3 generate `. + +### `generate bindings` +Generates bindings and models for your Go code. + +```bash +wails3 generate bindings [flags] [patterns...] +``` + +#### Flags +| Flag | Description | Default | +|-------------|---------------------------|---------------------| +| `-f` | Additional Go build flags | | +| `-d` | Output directory | `frontend/bindings` | +| `-models` | Models filename | `models` | +| `-index` | Index filename | `index` | +| `-ts` | Generate TypeScript | `false` | +| `-i` | Use TS interfaces | `false` | +| `-b` | Use bundled runtime | `false` | +| `-names` | Use names instead of IDs | `false` | +| `-noindex` | Skip index files | `false` | +| `-dry` | Dry run | `false` | +| `-silent` | Silent mode | `false` | +| `-v` | Debug output | `false` | +| `-clean` | Clean output directory | `false` | + +### `generate build-assets` +Generates build assets for your application. + +```bash +wails3 generate build-assets [flags] +``` + +#### Flags +| Flag | Description | Default | +|----------------|---------------------|--------------------| +| `-name` | Project name | | +| `-dir` | Output directory | `build` | +| `-silent` | Suppress output | `false` | +| `-company` | Company name | | +| `-productname` | Product name | | +| `-description` | Product description | | +| `-version` | Product version | | +| `-identifier` | Product identifier | `com.wails.[name]` | +| `-copyright` | Copyright notice | | +| `-comments` | File comments | | + +### `generate icons` +Generates application icons. + +```bash +wails3 generate icons [flags] +``` + +#### Flags +| Flag | Description | Default | +|--------------------|------------------------------|-----------------------| +| `-input` | Input PNG file | Required | +| `-windowsfilename` | Windows output filename | | +| `-macfilename` | macOS output filename | | +| `-sizes` | Icon sizes (comma-separated) | `256,128,64,48,32,16` | +| `-example` | Generate example icon | `false` | + +### `generate syso` +Generates Windows .syso file. + +```bash +wails3 generate syso [flags] +``` + +#### Flags +| Flag | Description | Default | +|-------------|---------------------------|----------------------------| +| `-manifest` | Path to manifest file | Required | +| `-icon` | Path to icon file | Required | +| `-info` | Path to version info file | | +| `-arch` | Target architecture | Current GOARCH | +| `-out` | Output filename | `rsrc_windows_[arch].syso` | + +### `generate .desktop` +Generates a Linux .desktop file. + +```bash +wails3 generate .desktop [flags] +``` + +#### Flags +| Flag | Description | Default | +|------------------|---------------------------|------------------| +| `-name` | Application name | Required | +| `-exec` | Executable path | Required | +| `-icon` | Icon path | | +| `-categories` | Application categories | `Utility` | +| `-comment` | Application comment | | +| `-terminal` | Run in terminal | `false` | +| `-keywords` | Search keywords | | +| `-version` | Application version | | +| `-genericname` | Generic name | | +| `-startupnotify` | Show startup notification | `false` | +| `-mimetype` | Supported MIME types | | +| `-output` | Output filename | `[name].desktop` | + +### `generate runtime` +Generates the pre-built version of the runtime. + +```bash +wails3 generate runtime +``` + +### `generate constants` +Generates JavaScript constants from Go code. + +```bash +wails3 generate constants +``` + +### `generate appimage` +Generates a Linux AppImage. + +```bash +wails3 generate appimage [flags] +``` + +#### Flags +| Flag | Description | Default | +|-------------|-----------------------|----------------| +| `-binary` | Path to binary | Required | +| `-icon` | Path to icon file | Required | +| `-desktop` | Path to .desktop file | Required | +| `-builddir` | Build directory | Temp directory | +| `-output` | Output directory | `.` | + +Base command: `wails3 service` + +## Service Commands + +Service commands help manage Wails services. All service commands use the base command: `wails3 service `. + +### `service init` +Initializes a new service. + +```bash +wails3 service init [flags] +``` + +#### Flags +| Flag | Description | Default | +|------|---------------------|-------------------| +| `-n` | Service name | `example_service` | +| `-d` | Service description | `Example service` | +| `-p` | Package name | | +| `-o` | Output directory | `.` | +| `-q` | Suppress output | `false` | +| `-a` | Author name | | +| `-v` | Version | | +| `-w` | Website URL | | +| `-r` | Repository URL | | +| `-l` | License | | + +Base command: `wails3 tool` + +## Tool Commands + +Tool commands provide utilities for development and debugging. All tool commands use the base command: `wails3 tool `. + +### `tool checkport` +Checks if a port is open. Useful for testing if vite is running. + +```bash +wails3 tool checkport [flags] +``` + +#### Flags +| Flag | Description | Default | +|---------|---------------|-------------| +| `-port` | Port to check | `9245` | +| `-host` | Host to check | `localhost` | + +### `tool watcher` +Watches files and runs a command when they change. + +```bash +wails3 tool watcher [flags] +``` + +#### Flags +| Flag | Description | Default | +|------------|---------------------|----------------------| +| `-config` | Config file path | `./build/config.yml` | +| `-ignore` | Patterns to ignore | | +| `-include` | Patterns to include | | + +### `tool cp` +Copies files. + +```bash +wails3 tool cp +``` + +### `tool buildinfo` +Shows build information about the application. + +```bash +wails3 tool buildinfo +``` + +### `tool version` +Bumps a semantic version based on the provided flags. + +```bash +wails3 tool version [flags] +``` + +#### Flags +| Flag | Description | Default | +|---------------|--------------------------------------------------|---------| +| `-v` | Current version to bump | | +| `-major` | Bump major version | `false` | +| `-minor` | Bump minor version | `false` | +| `-patch` | Bump patch version | `false` | +| `-prerelease` | Bump prerelease version (e.g., alpha.5 to alpha.6) | `false` | + +The command follows the precedence order: major > minor > patch > prerelease. It preserves the "v" prefix if present in the input version, as well as any prerelease and metadata components. + +Example usage: +```bash +wails3 tool version -v 1.2.3 -major # Output: 2.0.0 +wails3 tool version -v v1.2.3 -minor # Output: v1.3.0 +wails3 tool version -v 1.2.3-alpha -patch # Output: 1.2.4-alpha +wails3 tool version -v v3.0.0-alpha.5 -prerelease # Output: v3.0.0-alpha.6 +``` + +### `tool package` +Generates Linux packages (deb, rpm, archlinux). + +```bash +wails3 tool package [flags] +``` + +#### Flags +| Flag | Description | Default | +|-----------|--------------------------------------|----------| +| `-format` | Package format (deb, rpm, archlinux) | `deb` | +| `-name` | Executable name | Required | +| `-config` | Config file path | Required | +| `-out` | Output directory | `.` | + +#### Flags +| Flag | Description | Default | +|-----------|--------------------------------------|---------| +| `-format` | Package format (deb, rpm, archlinux) | `deb` | +| `-name` | Executable name | `myapp` | +| `-config` | Config file path | | +| `-out` | Output directory | `.` | + +Base command: `wails3 update` + +## Update Commands + +Update commands help manage and update project assets. All update commands use the base command: `wails3 update `. + +### `update cli` +Updates the Wails CLI to a new version. + +```bash +wails3 update cli [flags] +``` + +#### Flags +| Flag | Description | Default | +|-------------|---------------------------------|---------| +| `-pre` | Update to latest pre-release | `false` | +| `-version` | Update to specific version | | +| `-nocolour` | Disable colored output | `false` | + +The update cli command allows you to update your Wails CLI installation. By default, it updates to the latest stable release. +You can use the `-pre` flag to update to the latest pre-release version, or specify a particular version using the `-version` flag. + +After updating, remember to update your project's go.mod file to use the same version: +```bash +require github.com/wailsapp/wails/v3 v3.x.x +``` + +### `update build-assets` +Updates the build assets using the given config file. + +```bash +wails3 update build-assets [flags] +``` + +#### Flags +| Flag | Description | Default | +|----------------|---------------------|---------| +| `-config` | Config file path | | +| `-dir` | Output directory | `build` | +| `-silent` | Suppress output | `false` | +| `-company` | Company name | | +| `-productname` | Product name | | +| `-description` | Product description | | +| `-version` | Product version | | +| `-identifier` | Product identifier | | +| `-copyright` | Copyright notice | | +| `-comments` | File comments | | + +Base command: `wails3` + +## Utility Commands + +Utility commands provide helpful shortcuts for common tasks. Use these commands directly with the base command: `wails3 `. + +### `docs` +Opens the Wails documentation in your default browser. + +```bash +wails3 docs +``` + +### `releasenotes` +Shows the release notes for the current or specified version. + +```bash +wails3 releasenotes [flags] +``` + +#### Flags +| Flag | Description | Default | +|------|-----------------------------------|---------| +| `-v` | Version to show release notes for | | +| `-n` | Disable colour output | `false` | + +### `version` +Prints the current version of Wails. + +```bash +wails3 version +``` + +### `sponsor` +Opens the Wails sponsorship page in your default browser. + +```bash +wails3 sponsor diff --git a/docs/src/content/docs/guides/custom-protocol-association.mdx b/docs/src/content/docs/guides/custom-protocol-association.mdx new file mode 100644 index 000000000..c6de13591 --- /dev/null +++ b/docs/src/content/docs/guides/custom-protocol-association.mdx @@ -0,0 +1,167 @@ +--- +title: Custom Protocol Schemes (Deep Linking) +description: Guide to implementing custom URL schemes for deep linking in Wails applications across macOS, Windows, and Linux. +--- + +import { Aside } from '@astrojs/starlight/components'; + +# Custom Protocol Schemes (Deep Linking) + +Custom protocol schemes (also known as custom URL schemes or deep linking) allow your Wails application to be launched or brought to the foreground by clicking a URL with a scheme you define (e.g., `myapp://some/data`). This is useful for various purposes, such as: + +- OAuth authentication flows. +- Inter-application communication. +- Launching your app with a specific context or to perform a particular action. + +Wails provides a unified way to handle these custom URL invocations across macOS, Windows, and Linux through the `events.Common.ApplicationLaunchedWithUrl` event. + +## Defining Your Protocols + +First, you need to define the custom protocol schemes your application will use. This is done in your `wails.json` project configuration file. Wails reads this file during the build process (`wails build`) to configure the necessary platform-specific assets like `Info.plist` for macOS, NSIS installer scripts for Windows, and `.desktop` files for Linux. + +**Example: `wails.json`** + +```json title="wails.json" +{ + "name": "My App", + "description": "An amazing Wails app!", + "info": { + "companyName": "My Company", + "productName": "My Product", + // ... other info fields ... + "protocols": [ + { + "scheme": "myapp", + "description": "My Application Custom Protocol" + }, + { + "scheme": "anotherprotocol", + "description": "Another protocol for specific actions" + } + ] + } + // ... other wails.json fields ... +} +``` + +This `info.protocols` array is what Wails uses to generate the necessary entries in platform-specific files. For example, in template files, you might access this via a path like `{{.Info.Protocols}}`. + + + +## Handling the Event in Your Application + +When your application is launched or activated via a custom URL, Wails emits an `events.Common.ApplicationLaunchedWithUrl` event. You can listen for this event and retrieve the URL that triggered the launch. + +```go title="main.go" +import ( + "log" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func main() { + app := application.New(application.Options{ + Name: "My App", // Ensure this matches relevant info from wails.json if needed + Description: "An amazing Wails app!", + // ... other runtime options ... + }) + + app.Event.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) { + launchedURL := e.Context().URL() // Retrieve the URL from the event context + log.Printf("Application launched with URL: %s", launchedURL) + + // TODO: Process the URL (e.g., navigate, perform action, etc.) + // Example: app.Event.Emit("frontend:ShowURL", launchedURL) + }) + + // ... rest of your main function ... + err := app.Run() + if err != nil { + log.Fatal(err) + } +} +``` + + + +## Platform-Specific Setup and Behavior + +While Wails aims for a unified event, the underlying mechanism for custom protocol registration and URL delivery varies by operating system. + +### macOS + +- **Setup:** Wails automatically configures your application's `Info.plist` file during the build process. It adds `CFBundleURLTypes` entries based on the `info.protocols` defined in your `wails.json` file. + ```xml title="Info.plist (excerpt generated by Wails)" + CFBundleURLTypes + + + CFBundleURLName + My Application Custom Protocol + CFBundleURLSchemes + + myapp + + + + + ``` +- **How it Works:** When a URL like `myapp://` is opened, macOS uses LaunchServices to find the application registered for that scheme and sends it an Apple Event (`kAEGetURL`). Wails intercepts this event and translates it into the common `events.Common.ApplicationLaunchedWithUrl` Wails event, providing the URL via `e.Context().URL()`. + +### Windows + +- **Setup:** Custom protocol schemes on Windows are registered in the Windows Registry. Wails facilitates this through its NSIS installer template. + - When you build your application with the `-nsis` flag, Wails uses the `v3/internal/commands/updatable_build_assets/windows/nsis/wails_tools.nsh.tmpl` file. + - This template contains macros like `CUSTOM_PROTOCOL_ASSOCIATE` and `wails.associateCustomProtocols` which use the `info.protocols` from your `wails.json` (passed as `{{.Info.Protocols}}` to the template) to create the necessary registry entries during installation. + ```nsis title="wails_tools.nsh.tmpl (excerpt)" + !macro wails.associateCustomProtocols + ; Create custom protocols associations + {{range .Info.Protocols}} + !insertmacro CUSTOM_PROTOCOL_ASSOCIATE "{{.Scheme}}" "{{.Description}}" "$INSTDIR\${PRODUCT_EXECUTABLE},0" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\"" + {{end}} + !macroend + ``` +- **How it Works:** The installer registers your application executable to be called with the URL as a command-line argument (`%1`). For example, `your_app.exe "myapp://some/data"`. + - The Wails runtime for Windows (`v3/pkg/application/application_windows.go`) has been updated to check `os.Args` upon startup. If it detects an argument that looks like a URL (e.g., `os.Args[1]` contains `"://"`), it now emits the `events.Common.ApplicationLaunchedWithUrl` event with this URL. + + + +### Linux + +- **Setup:** On Linux, custom protocol handling is typically managed via `.desktop` files and the MIME type system. + - Wails uses a `.desktop` file template (e.g., `v3/internal/commands/updatable_build_assets/linux/desktop.tmpl`) which is populated during the build using information from `wails.json`. + ```desktop title="desktop.tmpl (excerpt)" + [Desktop Entry] + Name={{.ProductName}} + Exec=/usr/local/bin/{{.BinaryName}} %u + MimeType={{range $index, $protocol := .Info.Protocols}}x-scheme-handler/{{$protocol.Scheme}};{{end}} + ``` + The `Exec` line uses `%u` which gets replaced by the URL. The `MimeType` line registers your application as a handler for `x-scheme-handler/your-scheme` for each protocol defined in `wails.json` (via `{{.Info.Protocols}}`). + - When packaging for Linux (e.g., using `nfpm`), this `.desktop` file is installed to `/usr/share/applications/`. + - A `postinstall.sh` script (e.g., `v3/internal/commands/build_assets/linux/nfpm/scripts/postinstall.sh`) is used to update the system's application and MIME databases: + ```sh title="postinstall.sh (excerpt)" + #!/bin/sh + update-desktop-database -q /usr/share/applications + update-mime-database -n /usr/share/mime + ``` +- **How it Works:** When a URL like `myapp://` is opened, the desktop environment uses the MIME database to find the associated `.desktop` file and executes the command specified in its `Exec` line, substituting `%u` with the URL. Your application receives this URL as a command-line argument. + - The Wails runtime for Linux (`v3/pkg/application/application_linux.go`) checks `os.Args` on startup. If it detects an argument that looks like a URL, it emits the `events.Common.ApplicationLaunchedWithUrl` event. + +## Testing Your Custom Protocols + +- **macOS:** Open Terminal and type `open "your-scheme://your/data"`. +- **Linux:** Open a terminal and type `xdg-open "your-scheme://your/data"` (requires `xdg-utils` to be installed and the app to be properly packaged and registered). +- **Windows:** After installation via NSIS: + - You can try running `start your-scheme://your/data` from Command Prompt or PowerShell. + - Create a simple HTML file with a link `Test Link` and open it in a browser. + + + +By following this guide, you can effectively use custom protocol schemes to enhance your Wails application's interactivity and integration with other applications or web services. diff --git a/docs/src/content/docs/guides/custom-templates.mdx b/docs/src/content/docs/guides/custom-templates.mdx new file mode 100644 index 000000000..3da561b35 --- /dev/null +++ b/docs/src/content/docs/guides/custom-templates.mdx @@ -0,0 +1,293 @@ +--- +title: Creating Custom Templates +description: Learn how to create and customise your own Wails v3 templates +sidebar: + order: 50 +--- + +This guide will walk you through the process of creating a custom template for Wails v3. + +## Why would I make a custom template? + +Wails comes with a number of pre-configured templates that allow you to get your application up and running quickly. But if you need a more customised setup, you can create your own template to suit your needs. This can then be shared with the Wails community for others to use. + +### 1. Generating a Template + +To create a custom template, you can use the `wails generate template` command: + +```bash +wails3 generate template -name mytemplate +``` + +This will create a new directory called "mytemplate" in your current directory. + +The `wails3 generate template` command supports the following options: + +| Option | Description | Default | +|----------------|---------------------------------------------------|-------------------| +| `-name` | The name of your template (required) | - | +| `-frontend` | Path to an existing frontend directory to include | - | +| `-author` | The author of the template | - | +| `-description` | A description of the template | - | +| `-helpurl` | URL for template documentation | - | +| `-dir` | Directory to generate the template in | Current directory | +| `-version` | Template version | v0.0.1 | + +For example, to create a template with all options: + +```bash +wails3 generate template \ + -name "My Custom Template" \ + -frontend ./my-existing-frontend \ + -author "Your Name" \ + -description "A template with my preferred setup" \ + -helpurl "https://github.com/yourusername/template-docs" \ + -dir ./templates \ + -version "v1.0.0" +``` + + :::tip + Using the `-frontend` option will copy an existing web frontend project into the template. + ::: + +### 2. Configure Template Metadata + +If you didn't specify the template configuration when generating the template, you can update the `template.json` file in the template directory: + +```json5 +{ + "name": "Your Template Name", // Display name of your template + "shortname": "template-shortname", // Used when referencing your template + "author": "Your Name", // Template author + "description": "Template description", // Template description + "helpurl": "https://your-docs.com", // Documentation URL + "version": "v0.0.1", // Template version + "schema": 3 // Must be kept as 3 for Wails v3 +} +``` +:::caution +The `schema` field must remain set to `3` for compatibility with Wails v3. +::: + +### 3. Set Up Build Tasks + +In the `build` directory is `Taskfile.yml` where you can define your template's build process. +This file uses [Task](https://taskfile.dev) for build automation. The key steps are: + +```yaml +tasks: + install:frontend:deps: + summary: Install frontend dependencies + 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 + + build:frontend: + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/* + deps: + - task: install:frontend:deps + - task: generate:bindings + cmds: + - npm run build -q + + +``` + +### 4. Frontend Setup + +If you did not use `-frontend` when generating the template, you need to add frontend files to your template. + +There are a number of ways to set up your frontend: starting from scratch or using an existing framework. + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + + + + If you want to start from scratch, you can create your frontend project just like you would for any web application. + The `frontend` directory in your template is just a regular directory where you can set up your preferred + development environment. You might want to use build tools like Vite, webpack, or even just plain HTML, CSS, and + JavaScript - it's entirely up to you! + + For example, if you're using Vite, you could navigate to the `frontend` directory and run: + + ```bash + npm create vite@latest . + ``` + + Then follow the prompts to set up your project exactly how you want it. The key thing to remember is that this is just a regular frontend project - you can use any tools, frameworks, or libraries you're familiar with. + + + For this example, we'll use [Vite](https://vitejs.dev/) to set up a React frontend project: + + ```bash + npm create vite@latest frontend -- --template react + cd frontend + npm install + ``` + + + + + +Now you have the frontend files in place, update `common/Taskfile.yml` with the appropriate commands: + ```yaml + tasks: + install:frontend:deps: + summary: Install frontend dependencies + 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 + + build:frontend: + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/* + deps: + - task: install:frontend:deps + - task: generate:bindings + cmds: + - npm run build -q + ``` + + :::note + For this example, the default Tasks do not need updating as they use the standard `npm install` and `npm run build` commands. + ::: + +### 5. Configure the Go Application + +The default files in the template directory are sufficient to get users started. However, you may want to provide some additional functionality to demonstrate your template's capabilities. The best way to do this is to rename `main.go.tmpl` to `main.go` and edit it like any other Go file. Once finished, ensure you rename it back to `main.go.tmpl` before committing your changes. If you do not care about having a templated `main.go` file (the default template injests the project name into the `Name` field of the application), you can skip this step. + +#### Template Variables + +Wails uses Go's templating engine to process files with the `.tmpl` extension. During template generation, several variables are available for use in your template files: + +| Variable | Description | Example | +|----------------------|----------------------------------|-----------------------------------| +| `Name` | The name of the project | `"MyApp"` | +| `BinaryName` | The name of the generated binary | `"myapp"` | +| `ProductName` | The product name | `"My Application"` | +| `ProductDescription` | Description of the product | `"An awesome application"` | +| `ProductVersion` | Version of the product | `"1.0.0"` | +| `ProductCompany` | Company name | `"My Company Ltd"` | +| `ProductCopyright` | Copyright information | `"Copyright 2024 My Company Ltd"` | +| `ProductComments` | Additional product comments | `"Built with Wails"` | +| `ProductIdentifier` | Unique product identifier | `"com.mycompany.myapp"` | +| `Typescript` | Whether TypeScript is being used | `true` or `false` | +| `WailsVersion` | The version of Wails being used | `"3.0.0"` | + +You can use these variables in your template files using Go's template syntax: + +```go +// main.go.tmpl +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "{{.ProductName}}", + Description: "{{.ProductDescription}}", + }) + // ... +} +``` + + :::tip + Templating can be applied to any file in your template, even html files, so long as the filename has `.tmpl` in its name. + ::: + +### 6. Testing Your Template + +To test your template: + +1. Generate a project using your template: `wails3 init -n testproject -t path/to/your/template` +2. Run `wails3 build` to generate the production build and make sure the binary in `bin` runs correctly +3. Run `wails3 dev` to start the development server. +4. Test that changes to the frontend code are reflected in the application. +5. Test that changes to the Go code rebuild and relaunch the application + +### 7. Sharing Your Template + +Once your template is ready, you can share it with the community by hosting it on GitHub. Here's how: + +1. Create a new GitHub repository for your template +2. Push your template code to the repository +3. Tag your releases using semantic versioning (e.g., v1.0.0) + +Users can then use your template directly from GitHub using the HTTPS URL: + +```bash +wails3 init -n myapp -t https://github.com/yourusername/your-template +``` + +You can also specify a particular version using the URL format: + +```bash +# Use a specific version tag +wails3 init -n myapp -t https://github.com/yourusername/your-template/releases/tag/v1.0.0 + +# Use a specific branch +wails3 init -n myapp -t https://github.com/yourusername/your-template/tree/main +``` + +To test your template before sharing: + +1. Push your changes to GitHub +2. Create a new test project using the HTTPS URL: + ```bash + wails3 init -n testapp -t https://github.com/yourusername/your-template + ``` +3. Verify that all files are correctly generated +4. Test the build and development workflow as described in the testing section + + :::note + Make sure your repository is public if you want others to use your template. + ::: + +For more information, visit the [Wails documentation](https://wails.io) + + +## Best Practices + +Let's talk about some key practices that will help make your template more useful and maintainable. Here are the main areas to focus on: + +1. **Make Your Template Easy to Understand** + - Write a clear, helpful README.md that gets users started quickly + - Add comments in your config files to explain the "why" behind your choices + - Show examples of common customisations - users love to see real-world use cases! + +2. **Keep Dependencies Happy** + - Stay on top of your frontend package updates + - Lock down specific versions in package.json to avoid surprises + - Let users know upfront what they'll need to have installed + +3. **Love Your Template** + - Keep it fresh with regular updates + - Give it a thorough test drive before sharing + - Share it with the Wails community - we'd love to see what you create! diff --git a/docs/src/content/docs/guides/customising-windows.mdx b/docs/src/content/docs/guides/customising-windows.mdx new file mode 100644 index 000000000..915439a28 --- /dev/null +++ b/docs/src/content/docs/guides/customising-windows.mdx @@ -0,0 +1,129 @@ +--- +title: Customising Windows in Wails +sidebar: + order: 10 +--- + +import {Badge} from '@astrojs/starlight/components'; + +Relevant Platforms: +
+ +Wails provides an API to control the appearance and functionality of the +controls of a window. This functionality is available on Windows and macOS, but +not on Linux. + +## Setting the Window Button States + +The button states are defined by the `ButtonState` enum: + +```go +type ButtonState int + +const ( + ButtonEnabled ButtonState = 0 + ButtonDisabled ButtonState = 1 + ButtonHidden ButtonState = 2 +) +``` + +- `ButtonEnabled`: The button is enabled and visible. +- `ButtonDisabled`: The button is visible but disabled (grayed out). +- `ButtonHidden`: The button is hidden from the titlebar. + +The button states can be set during window creation or at runtime. + +### Setting Button States During Window Creation + +When creating a new window, you can set the initial state of the buttons using +the `WebviewWindowOptions` struct: + +```go title="main.go" +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "My Application", + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MinimiseButtonState: application.ButtonHidden, + MaximiseButtonState: application.ButtonDisabled, + CloseButtonState: application.ButtonEnabled, + }) + + app.Run() +} +``` + +In the example above, the minimise button is hidden, the maximise button is +inactive (grayed out), and the close button is active. + +### Setting Button States at Runtime + +You can also change the button states at runtime using the following methods on +the `Window` interface: + +```go +window.SetMinimiseButtonState(wails.ButtonHidden) +window.SetMaximiseButtonState(wails.ButtonEnabled) +window.SetCloseButtonState(wails.ButtonDisabled) +``` + +### Platform Differences + +The button state functionality behaves slightly differently on Windows and +macOS: + +| | Windows | Mac | +|-----------------------|------------------------|------------------------| +| Disable Min/Max/Close | Disables Min/Max/Close | Disables Min/Max/Close | +| Hide Min | Disables Min | Hides Min button | +| Hide Max | Disables Max | Hides Max button | +| Hide Close | Hides all controls | Hides Close | + +Note: On Windows, it is not possible to hide the Min/Max buttons individually. +However, disabling both will hide both of the controls and only show the close +button. + +### Controlling Window Style (Windows) + +To control the style of the titlebar on Windows, you can use the `ExStyle` field +in the `WebviewWindowOptions` struct: + +Example: + +```go title="main.go" +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/w32" +) + +func main() { + app := application.New(application.Options{ + Name: "My Application", + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Windows: application.WindowsWindow{ + ExStyle: w32.WS_EX_TOOLWINDOW | w32.WS_EX_NOREDIRECTIONBITMAP | w32.WS_EX_TOPMOST, + }, + }) + + app.Run() +} +``` + +Other options that affect the Extended Style of a window will be overridden by +this setting: + +- HiddenOnTaskbar +- AlwaysOnTop +- IgnoreMouseEvents +- BackgroundType diff --git a/docs/src/content/docs/guides/events-reference.mdx b/docs/src/content/docs/guides/events-reference.mdx new file mode 100644 index 000000000..7f4028120 --- /dev/null +++ b/docs/src/content/docs/guides/events-reference.mdx @@ -0,0 +1,410 @@ +--- +title: Events Guide +description: A practical guide to using events in Wails v3 for application communication and lifecycle management +--- + +**NOTE: This guide is a work in progress** + +# Events Guide + +Events are the heartbeat of communication in Wails applications. They allow different parts of your application to talk to each other without being tightly coupled. This guide will walk you through everything you need to know about using events effectively in your Wails application. + +## Understanding Wails Events + +Think of events as messages that get broadcast throughout your application. Any part of your application can listen for these messages and react accordingly. This is particularly useful for: + +- **Responding to window changes**: Know when your window is minimized, maximized, or moved +- **Handling system events**: React to theme changes or power events +- **Custom application logic**: Create your own events for features like data updates or user actions +- **Cross-component communication**: Let different parts of your app communicate without direct dependencies + +## Event Naming Convention + +All Wails events follow a namespace pattern to clearly indicate their origin: + +- `common:` - Cross-platform events that work on Windows, macOS, and Linux +- `windows:` - Windows-specific events +- `mac:` - macOS-specific events +- `linux:` - Linux-specific events + +For example: +- `common:WindowFocus` - Window gained focus (works everywhere) +- `windows:APMSuspend` - System is suspending (Windows only) +- `mac:ApplicationDidBecomeActive` - App became active (macOS only) + +## Getting Started with Events + +### Listening to Events (Frontend) + +The most common use case is listening for events in your frontend code: + +```javascript +import { Events } from '@wailsio/runtime'; + +// Listen for when the window gains focus +Events.On('common:WindowFocus', () => { + console.log('Window is now focused!'); + // Maybe refresh some data or resume animations +}); + +// Listen for theme changes +Events.On('common:ThemeChanged', (event) => { + console.log('Theme changed:', event.data); + // Update your app's theme accordingly +}); + +// Listen for custom events from your Go backend +Events.On('my-app:data-updated', (event) => { + console.log('Data updated:', event.data); + // Update your UI with the new data +}); +``` + +### Emitting Events (Backend) + +From your Go code, you can emit events that your frontend can listen to: + +```go +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" + "time" +) + +func (s *Service) UpdateData() { + // Do some data processing... + + // Notify the frontend + app := application.Get() + app.Event.EmitEvent(&application.CustomEvent{ + Name: "my-app:data-updated", + Data: map[string]interface{}{ + "timestamp": time.Now(), + "count": 42, + }, + }) +} +``` + +### Removing Event Listeners + +Always clean up your event listeners when they're no longer needed: + +```javascript +// Store the handler reference +const focusHandler = () => { + console.log('Window focused'); +}; + +// Add the listener +Events.On('common:WindowFocus', focusHandler); + +// Later, remove it when no longer needed +Events.Off('common:WindowFocus', focusHandler); + +// Or remove all listeners for an event +Events.Off('common:WindowFocus'); +``` + +## Common Use Cases + +### 1. Pause/Resume on Window Focus + +Many applications need to pause certain activities when the window loses focus: + +```javascript +let animationRunning = true; + +Events.On('common:WindowLostFocus', () => { + animationRunning = false; + pauseBackgroundTasks(); +}); + +Events.On('common:WindowFocus', () => { + animationRunning = true; + resumeBackgroundTasks(); +}); +``` + +### 2. Responding to Theme Changes + +Keep your app in sync with the system theme: + +```javascript +Events.On('common:ThemeChanged', (event) => { + const isDarkMode = event.data.isDark; + + if (isDarkMode) { + document.body.classList.add('dark-theme'); + document.body.classList.remove('light-theme'); + } else { + document.body.classList.add('light-theme'); + document.body.classList.remove('dark-theme'); + } +}); +``` + +### 3. Handling File Drops + +Make your app accept dragged files: + +```javascript +Events.On('common:WindowFilesDropped', (event) => { + const files = event.data.files; + + files.forEach(file => { + console.log('File dropped:', file); + // Process the dropped files + handleFileUpload(file); + }); +}); +``` + +### 4. Window Lifecycle Management + +Respond to window state changes: + +```javascript +Events.On('common:WindowClosing', () => { + // Save user data before closing + saveApplicationState(); + + // You could also prevent closing by returning false + // from a registered window close handler +}); + +Events.On('common:WindowMaximise', () => { + // Adjust UI for maximized view + adjustLayoutForMaximized(); +}); + +Events.On('common:WindowRestore', () => { + // Return UI to normal state + adjustLayoutForNormal(); +}); +``` + +### 5. Platform-Specific Features + +Handle platform-specific events when needed: + +```javascript +// Windows-specific power management +Events.On('windows:APMSuspend', () => { + console.log('System is going to sleep'); + saveState(); +}); + +Events.On('windows:APMResumeSuspend', () => { + console.log('System woke up'); + refreshData(); +}); + +// macOS-specific app lifecycle +Events.On('mac:ApplicationWillTerminate', () => { + console.log('App is about to quit'); + performCleanup(); +}); +``` + +## Creating Custom Events + +You can create your own events for application-specific needs: + +### Backend (Go) + +```go +// Emit a custom event when data changes + +func (s *Service) ProcessUserData(userData UserData) error { + // Process the data... + + app := application.Get() + // Notify all listeners + app.Event.EmitEvent(&application.CustomEvent{ + Name: "user:data-processed", + Data: map[string]interface{}{ + "userId": userData.ID, + "status": "completed", + "timestamp": time.Now(), + }, + }) + + return nil +} + +// Emit periodic updates +func (s *Service) StartMonitoring() { + app := application.Get() + ticker := time.NewTicker(5 * time.Second) + go func() { + for range ticker.C { + stats := s.collectStats() + app.Event.Emit("monitor:stats-updated", stats) + } + }() +} +``` + +### Frontend (JavaScript) + +```javascript +// Listen for your custom events +Events.On('user:data-processed', (event) => { + const { userId, status, timestamp } = event.data; + + showNotification(`User ${userId} processing ${status}`); + updateUIWithNewData(); +}); + +Events.On('monitor:stats-updated', (event) => { + updateDashboard(event.data); +}); +``` + +## Event Reference + +### Common Events (Cross-platform) + +These events work on all platforms: + +| Event | Description | When to Use | +|-------|-------------|-------------| +| `common:ApplicationStarted` | Application has fully started | Initialize your app, load saved state | +| `common:WindowRuntimeReady` | Wails runtime is ready | Start making Wails API calls | +| `common:ThemeChanged` | System theme changed | Update app appearance | +| `common:WindowFocus` | Window gained focus | Resume activities, refresh data | +| `common:WindowLostFocus` | Window lost focus | Pause activities, save state | +| `common:WindowMinimise` | Window was minimized | Pause rendering, reduce resource usage | +| `common:WindowMaximise` | Window was maximized | Adjust layout for full screen | +| `common:WindowRestore` | Window restored from min/max | Return to normal layout | +| `common:WindowClosing` | Window is about to close | Save data, cleanup resources | +| `common:WindowFilesDropped` | Files dropped on window | Handle file imports | +| `common:WindowDidResize` | Window was resized | Adjust layout, rerender charts | +| `common:WindowDidMove` | Window was moved | Update position-dependent features | + +### Platform-Specific Events + +#### Windows Events + +Key events for Windows applications: + +| Event | Description | Use Case | +|-------|-------------|----------| +| `windows:SystemThemeChanged` | Windows theme changed | Update app colors | +| `windows:APMSuspend` | System suspending | Save state, pause operations | +| `windows:APMResumeSuspend` | System resumed | Restore state, refresh data | +| `windows:APMPowerStatusChange` | Power status changed | Adjust performance settings | + +#### macOS Events + +Important macOS application events: + +| Event | Description | Use Case | +|-------|-------------|----------| +| `mac:ApplicationDidBecomeActive` | App became active | Resume operations | +| `mac:ApplicationDidResignActive` | App became inactive | Pause operations | +| `mac:ApplicationWillTerminate` | App will quit | Final cleanup | +| `mac:WindowDidEnterFullScreen` | Entered fullscreen | Adjust UI for fullscreen | +| `mac:WindowDidExitFullScreen` | Exited fullscreen | Restore normal UI | + +#### Linux Events + +Core Linux window events: + +| Event | Description | Use Case | +|-------|-------------|----------| +| `linux:SystemThemeChanged` | Desktop theme changed | Update app theme | +| `linux:WindowFocusIn` | Window gained focus | Resume activities | +| `linux:WindowFocusOut` | Window lost focus | Pause activities | + +## Best Practices + +### 1. Use Event Namespaces + +When creating custom events, use namespaces to avoid conflicts: + +```javascript +// Good - namespaced events +Events.Emit('myapp:user:login'); +Events.Emit('myapp:data:updated'); +Events.Emit('myapp:network:connected'); + +// Avoid - generic names that might conflict +Events.Emit('login'); +Events.Emit('update'); +``` + +### 2. Clean Up Listeners + +Always remove event listeners when components unmount: + +```javascript +// React example +useEffect(() => { + const handler = (event) => { + // Handle event + }; + + Events.On('common:WindowResize', handler); + + // Cleanup + return () => { + Events.Off('common:WindowResize', handler); + }; +}, []); +``` + +### 3. Handle Platform Differences + +Check platform availability when using platform-specific events: + +```javascript +import { Platform } from '@wailsio/runtime'; + +if (Platform.isWindows) { + Events.On('windows:APMSuspend', handleSuspend); +} else if (Platform.isMac) { + Events.On('mac:ApplicationWillTerminate', handleTerminate); +} +``` + +### 4. Don't Overuse Events + +While events are powerful, don't use them for everything: + +- ✅ Use events for: System notifications, lifecycle changes, broadcast updates +- ❌ Avoid events for: Direct function returns, single component updates, synchronous operations + +## Debugging Events + +To debug event issues: + +```javascript +// Log all events (development only) +if (isDevelopment) { + const originalOn = Events.On; + Events.On = function(eventName, handler) { + console.log(`[Event Registered] ${eventName}`); + return originalOn.call(this, eventName, function(event) { + console.log(`[Event Fired] ${eventName}`, event); + return handler(event); + }); + }; +} +``` + +## Source of Truth + +The complete list of available events can be found in the Wails source code: +- Frontend events: [`v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts`](https://github.com/wailsapp/wails/blob/main/v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts) +- Backend events: [`v3/pkg/events/events.go`](https://github.com/wailsapp/wails/blob/main/v3/pkg/events/events.go) + +Always refer to these files for the most up-to-date event names and availability. + +## Summary + +Events in Wails provide a powerful, decoupled way to handle communication in your application. By following the patterns and practices in this guide, you can build responsive, platform-aware applications that react smoothly to system changes and user interactions. + +Remember: start with common events for cross-platform compatibility, add platform-specific events when needed, and always clean up your event listeners to prevent memory leaks. \ No newline at end of file diff --git a/docs/src/content/docs/guides/file-associations.mdx b/docs/src/content/docs/guides/file-associations.mdx new file mode 100644 index 000000000..ba37d9f35 --- /dev/null +++ b/docs/src/content/docs/guides/file-associations.mdx @@ -0,0 +1,178 @@ +--- +title: File Associations +sidebar: + order: 20 +--- + +import { Steps } from "@astrojs/starlight/components"; +import {Badge} from '@astrojs/starlight/components'; + +Relevant Platforms: +
+ +File associations allow your application to handle specific file types when +users open them. This is particularly useful for text editors, image viewers, or +any application that works with specific file formats. This guide explains how +to implement file associations in your Wails v3 application. + +## Overview + +File association support in Wails v3 is currently available for: + +- Windows (NSIS installer packages) +- macOS (application bundles) + +## Configuration + +File associations are configured in the `config.yml` file located in your +project's `build` directory. + +### Basic Configuration + +To set up file associations: + +1. Open `build/config.yml` +2. Add your file associations under the `fileAssociations` section +3. Run `wails3 update build-assets` to update the build assets +4. Set the `FileAssociations` field in the application options +5. Package your application using `wails3 package` + +Here's an example configuration: + +```yaml +fileAssociations: + - ext: myapp + name: MyApp Document + description: MyApp Document File + iconName: myappFileIcon + role: Editor + - ext: custom + name: Custom Format + description: Custom File Format + iconName: customFileIcon + role: Editor +``` + +### Configuration Properties + +| Property | Description | Platform | +|-------------|------------------------------------------------------------------|----------| +| ext | File extension without the leading period (e.g., `txt`) | All | +| name | Display name for the file type | All | +| description | Description shown in file properties | Windows | +| iconName | Name of the icon file (without extension) in the build folder | All | +| role | Application's role for this file type (e.g., `Editor`, `Viewer`) | macOS | +| mimeType | MIME type for the file (e.g., `image/jpeg`) | macOS | + +## Listening for File Open Events + +To handle file open events in your application, you can listen for the +`events.Common.ApplicationOpenedWithFile` event: + +```go title="main.go" +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func main() { + app := application.New(application.Options{ + Name: "MyApp", + FileAssociations: []string{".txt", ".md"}, // Specify supported extensions + }) + + // Listen for files being used to open the application + app.Event.OnApplicationEvent(events.Common.ApplicationOpenedWithFile, func(event *application.ApplicationEvent) { + associatedFile := event.Context().Filename() + application.InfoDialog().SetMessage("Application opened with file: " + associatedFile).Show() + }) + + // Create your window and run the app... +} + +``` + +## Step-by-Step Tutorial + +Let's walk through setting up file associations for a simple text editor: + + + +1. ### Create Icons + + - Create icons for your file type (recommended sizes: 16x16, 32x32, 48x48, + 256x256) + - Save the icons in your project's `build` folder + - Name them according to your `iconName` configuration (e.g., + `textFileIcon.png`) + + :::tip + You can use `wails3 generate icons` to generate the required icons for you. + Run `wails3 generate icons --help` for more information. + ::: + + - For macOS add copy statement like `cp build/darwin/documenticon.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources` in the `create:app:bundle:` task. + +2. ### Configure File Associations + + Edit the `build/config.yml` file to add your file associations: + + ```yaml + # build/config.yml + fileAssociations: + - ext: txt + name: Text Document + description: Plain Text Document + iconName: textFileIcon + role: Editor + ``` + +3. ### Update Build Assets + + Run the following command to update the build assets: + + ```bash + wails3 update build-assets + ``` + +4. ### Set File Associations in the Application Options + + In your `main.go` file, set the `FileAssociations` field in the application + options: + + ```go + app := application.New(application.Options{ + Name: "MyApp", + FileAssociations: []string{".txt", ".md"}, // Specify supported extensions + }) + ``` + + :::tip[Why are file extensions required in both the application config and config.yml?] + + On Windows, when a file is opened with a file association, the application is + launched with the filename as the first argument to the application. The + application has no way of knowing if the first argument is a file or a command + line argument, so it uses the `FileAssociations` field in the application + options to determine if the first argument is an associated file or not. + + ::: + +5. ### Package Your Application + + Package your application using the following command: + + ```bash + wails3 package + ``` + + The packaged application will be created in the `bin` directory. You can then + install and test the application. + + ## Additional Notes + + - Icons should be provided in PNG format in the build folder + - Testing file associations requires installing the packaged application + + \ No newline at end of file diff --git a/docs/src/content/docs/guides/gin-routing.mdx b/docs/src/content/docs/guides/gin-routing.mdx new file mode 100644 index 000000000..c4704a4bb --- /dev/null +++ b/docs/src/content/docs/guides/gin-routing.mdx @@ -0,0 +1,583 @@ +--- +title: Using Gin for Routing +description: A comprehensive guide to integrating Gin web framework with Wails v3 applications +--- + +This guide demonstrates how to integrate the [Gin web framework](https://github.com/gin-gonic/gin) with Wails v3. Gin is a high-performance HTTP web framework written in Go that makes it easy to build web applications and APIs. + +## Introduction + +Wails v3 provides a flexible asset system that allows you to use any HTTP handler, including popular web frameworks like Gin. This integration enables you to: + +- Serve web content using Gin's powerful routing and middleware capabilities +- Create RESTful APIs that can be accessed from your Wails application +- Leverage Gin's extensive feature set whilst maintaining the benefits of Wails + +## Setting Up Gin with Wails + +To integrate Gin with Wails, you need to create a Gin router and configure it as the asset handler in your Wails application. Here's a step-by-step guide: + +### 1. Install Dependencies + +First, ensure you have the Gin package installed: + +```bash +go get -u github.com/gin-gonic/gin +``` + +### 2. Create a Middleware for Gin + +Create a middleware function that will handle the integration between Wails and Gin: + +```go +// GinMiddleware creates a middleware that passes requests to Gin if they're not handled by Wails +func GinMiddleware(ginEngine *gin.Engine) application.Middleware { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Let Wails handle the `/wails` route + if strings.HasPrefix(r.URL.Path, "/wails") { + next.ServeHTTP(w, r) + return + } + // Let Gin handle everything else + ginEngine.ServeHTTP(w, r) + }) + } +} +``` + +This middleware passes all HTTP requests to the Gin router. + +### 3. Configure Your Gin Router + +Set up your Gin router with routes, middlewares, and handlers: + +```go +// Create a new Gin router +ginEngine := gin.New() // Using New() instead of Default() to add custom middleware + +// Add middlewares +ginEngine.Use(gin.Recovery()) +ginEngine.Use(LoggingMiddleware()) // Your custom middleware + +// Define routes +ginEngine.GET("/", func(c *gin.Context) { + // Serve your main page +}) + +ginEngine.GET("/api/hello", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "Hello from Gin API!", + "time": time.Now().Format(time.RFC3339), + }) +}) +``` + +### 4. Integrate with Wails Application + +Configure your Wails application to use the Gin router as its asset handler: + +```go +// Create a new Wails application +app := application.New(application.Options{ + Name: "Gin Example", + Description: "A demo of using Gin with Wails", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: ginEngine, + Middleware: GinMiddleware(ginEngine), + }, +}) + +// Create window +app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Wails + Gin Example", + Width: 900, + Height: 700, + URL: "/", // This will load the route handled by Gin +}) +``` + +## Serving Static Content + +There are several ways to serve static content with Gin in a Wails application: + +### Option 1: Using Go's embed Package + +The recommended approach is to use Go's `embed` package to embed static files into your binary: + +```go +//go:embed static +var staticFiles embed.FS + +// In your main function: +ginEngine.StaticFS("/static", http.FS(staticFiles)) + +// Serve index.html +ginEngine.GET("/", func(c *gin.Context) { + file, err := staticFiles.ReadFile("static/index.html") + if err != nil { + c.String(http.StatusInternalServerError, "Error reading index.html") + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", file) +}) +``` + +### Option 2: Serving from Disk + +For development purposes, you might prefer to serve files directly from disk: + +```go +// Serve static files from the "static" directory +ginEngine.Static("/static", "./static") + +// Serve index.html +ginEngine.GET("/", func(c *gin.Context) { + c.File("./static/index.html") +}) +``` + +## Custom Middleware + +Gin allows you to create custom middleware for various purposes. Here's an example of a logging middleware: + +```go +// LoggingMiddleware is a Gin middleware that logs request details +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Start timer + startTime := time.Now() + + // Process request + c.Next() + + // Calculate latency + latency := time.Since(startTime) + + // Log request details + log.Printf("[GIN] %s | %s | %s | %d | %s", + c.Request.Method, + c.Request.URL.Path, + c.ClientIP(), + c.Writer.Status(), + latency, + ) + } +} +``` + +## Handling API Requests + +Gin makes it easy to create RESTful APIs. Here's how to define API endpoints: + +```go +// GET endpoint +ginEngine.GET("/api/users", func(c *gin.Context) { + c.JSON(http.StatusOK, users) +}) + +// POST endpoint with JSON binding +ginEngine.POST("/api/users", func(c *gin.Context) { + var newUser User + if err := c.ShouldBindJSON(&newUser); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + // Process the new user... + c.JSON(http.StatusCreated, newUser) +}) + +// Path parameters +ginEngine.GET("/api/users/:id", func(c *gin.Context) { + id := c.Param("id") + // Find user by ID... + c.JSON(http.StatusOK, user) +}) + +// Query parameters +ginEngine.GET("/api/search", func(c *gin.Context) { + query := c.DefaultQuery("q", "") + limit := c.DefaultQuery("limit", "10") + // Perform search... + c.JSON(http.StatusOK, results) +}) +``` + +## Event Communication + +One of the powerful features of Wails is its event system, which allows for communication between the frontend and backend. When using Gin as a service, you can still leverage this event system by serving the Wails runtime.js file to your frontend. + +### Serving the Wails Runtime + +To enable event communication, you need to serve the Wails runtime.js file at the `/wails/runtime.js` path. Here's how to implement this in your Gin service: + +```go +import ( + "io" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/runtime" +) + +// GinService implements a Wails service that uses Gin for HTTP handling +type GinService struct { + ginEngine *gin.Engine + app *application.App + // Other fields... +} + +// ServeHTTP implements the http.Handler interface +func (s *GinService) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Special handling for Wails runtime.js + if r.URL.Path == "/wails/runtime.js" { + s.serveWailsRuntime(w, r) + return + } + + // All other requests go to the Gin router + s.ginEngine.ServeHTTP(w, r) +} + +// serveWailsRuntime serves the Wails runtime.js file +func (s *GinService) serveWailsRuntime(w http.ResponseWriter, r *http.Request) { + // Open the runtime.js file from the public runtime package + runtimeFile, err := runtime.Assets.Open(runtime.RuntimeJSPath) + if err != nil { + http.Error(w, "Failed to access runtime assets", http.StatusInternalServerError) + return + } + defer runtimeFile.Close() + + // Set the content type + w.Header().Set("Content-Type", "application/javascript") + + // Copy the file to the response + _, err = io.Copy(w, runtimeFile) + if err != nil { + http.Error(w, "Failed to serve runtime.js", http.StatusInternalServerError) + } +} +``` + +### Handling Events + +You'll also need to add an endpoint to handle events from the frontend. This endpoint will bridge the gap between the HTTP requests and the Wails event system: + +```go +// In your setupRoutes method +func (s *GinService) setupRoutes() { + // Event handling endpoint + s.ginEngine.POST("/events/emit", func(c *gin.Context) { + var eventData struct { + Name string `json:"name" binding:"required"` + Data interface{} `json:"data"` + } + + if err := c.ShouldBindJSON(&eventData); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Process the event using the Wails event system + s.app.Event.Emit(eventData.Name, eventData.Data) + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "Event processed successfully", + }) + }) + + // Other routes... +} +``` + +### Using Events in the Frontend + +In your frontend HTML, include the Wails runtime.js script and use the event API: + +```html + + + + + + + +

+    
+    
+
+
+```
+
+This approach allows you to use the Wails event system seamlessly with your Gin service, providing a consistent experience across your application.
+
+## Interacting with Wails
+
+Your Gin-served web content can interact with Wails features like events and bindings. To enable this interaction, you
+must use the JavaScript API package `@wailsio/runtime`.
+
+### Handling Wails Events in Go
+
+```go
+// Register event handler
+app.Event.On("my-event", func(event *application.CustomEvent) {
+    log.Printf("Received event from frontend: %v", event.Data)
+    // Process the event...
+})
+```
+
+### Emitting Events from JavaScript
+
+```html
+    
+```
+
+## Advanced Configuration
+
+### Customising Gin's Mode
+
+Gin has three modes: debug, release, and test. For production applications, you should use release mode:
+
+```go
+// Set Gin to release mode
+gin.SetMode(gin.ReleaseMode)
+
+// Create a new Gin router
+ginEngine := gin.New()
+```
+
+You can use Go's build tags to set the mode based on the build environment:
+
+```go[main_prod.go]
+// +build production
+
+var ginMode = gin.ReleaseMode
+```
+
+```go[main_dev.go]
+// +build !production
+
+var ginMode = gin.DebugMode
+```
+
+```go [main.go]
+// In your main function:
+gin.SetMode(ginMode)
+```
+
+### Handling WebSockets
+
+You can integrate WebSockets with Gin using libraries like Gorilla WebSocket:
+
+```go
+import "github.com/gorilla/websocket"
+
+var upgrader = websocket.Upgrader{
+    ReadBufferSize:  1024,
+    WriteBufferSize: 1024,
+    CheckOrigin: func(r *http.Request) bool {
+        return true // Allow all connections
+    },
+}
+
+// In your route handler:
+ginEngine.GET("/ws", func(c *gin.Context) {
+    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
+    if err != nil {
+        log.Println(err)
+        return
+    }
+    defer conn.Close()
+    
+    // Handle WebSocket connection...
+})
+```
+
+## Complete Example
+
+Here's a complete example of integrating Gin with Wails:
+
+```go
+package main
+
+import (
+	"embed"
+	"log"
+	"net/http"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/wailsapp/wails/v3/pkg/application"
+)
+
+//go:embed static
+var staticFiles embed.FS
+
+// GinMiddleware creates a middleware that passes requests to Gin if they're not handled by Wails
+func GinMiddleware(ginEngine *gin.Engine) application.Middleware {
+	return func(next http.Handler) http.Handler {
+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			// Let Wails handle the `/wails` route
+			if r.URL.Path == "/wails" {
+				next.ServeHTTP(w, r)
+				return
+			}
+			// Let Gin handle everything else
+			ginEngine.ServeHTTP(w, r)
+		})
+	}
+}
+
+// LoggingMiddleware is a Gin middleware that logs request details
+func LoggingMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		// Start timer
+		startTime := time.Now()
+
+		// Process request
+		c.Next()
+
+		// Calculate latency
+		latency := time.Since(startTime)
+
+		// Log request details
+		log.Printf("[GIN] %s | %s | %s | %d | %s",
+			c.Request.Method,
+			c.Request.URL.Path,
+			c.ClientIP(),
+			c.Writer.Status(),
+			latency,
+		)
+	}
+}
+
+func main() {
+	// Create a new Gin router
+	ginEngine := gin.New() // Using New() instead of Default() to add our own middleware
+
+	// Add middlewares
+	ginEngine.Use(gin.Recovery())
+	ginEngine.Use(LoggingMiddleware())
+
+	// Serve embedded static files
+	ginEngine.StaticFS("/static", http.FS(staticFiles))
+
+	// Define routes
+	ginEngine.GET("/", func(c *gin.Context) {
+		file, err := staticFiles.ReadFile("static/index.html")
+		if err != nil {
+			c.String(http.StatusInternalServerError, "Error reading index.html")
+			return
+		}
+		c.Data(http.StatusOK, "text/html; charset=utf-8", file)
+	})
+
+	ginEngine.GET("/api/hello", func(c *gin.Context) {
+		c.JSON(http.StatusOK, gin.H{
+			"message": "Hello from Gin API!",
+			"time":    time.Now().Format(time.RFC3339),
+		})
+	})
+
+	// Create a new Wails application
+	app := application.New(application.Options{
+		Name:        "Gin Example",
+		Description: "A demo of using Gin with Wails",
+		Mac: application.MacOptions{
+			ApplicationShouldTerminateAfterLastWindowClosed: true,
+		},
+		Assets: application.AssetOptions{
+			Handler:    ginEngine,
+			Middleware: GinMiddleware(ginEngine),
+		},
+	})
+
+	// Register event handler
+	app.Event.On("gin-button-clicked", func(event *application.CustomEvent) {
+		log.Printf("Received event from frontend: %v", event.Data)
+	})
+
+	// Create window
+	app.Window.NewWithOptions(application.WebviewWindowOptions{
+		Title:  "Wails + Gin Example",
+		Width:  900,
+		Height: 700,
+		URL:    "/",
+	})
+
+	// Run the app
+	err := app.Run()
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+## Best Practices
+
+- **Use Go's embed Package:** Embed static files into your binary for better distribution.
+- **Separate Concerns:** Keep your API logic separate from your UI logic.
+- **Error Handling:** Implement proper error handling in both Gin routes and frontend code.
+- **Security:** Be mindful of security considerations, especially when handling user input.
+- **Performance:** Use Gin's release mode in production for better performance.
+- **Testing:** Write tests for your Gin routes using Gin's testing utilities.
+
+## Conclusion
+
+Integrating Gin with Wails provides a powerful combination for building desktop applications with web technologies. Gin's performance and feature set complement Wails' desktop integration capabilities, allowing you to create sophisticated applications that leverage the best of both worlds.
+
+For more information, refer to the [Gin documentation](https://github.com/gin-gonic/gin) and the [Wails documentation](https://wails.io).
diff --git a/docs/src/content/docs/guides/gin-services.mdx b/docs/src/content/docs/guides/gin-services.mdx
new file mode 100644
index 000000000..89c4b68f5
--- /dev/null
+++ b/docs/src/content/docs/guides/gin-services.mdx
@@ -0,0 +1,561 @@
+---
+title: Using Gin for Services
+description: A guide to integrating the Gin web framework with Wails v3 Services
+---
+
+# Using Gin for Services
+
+The Gin web framework is a popular choice for building HTTP services in Go. With Wails v3, you can easily integrate Gin-based services into your application, providing a powerful way to handle HTTP requests, implement RESTful APIs, and serve web content.
+
+This guide will walk you through creating a Gin-based service that can be mounted at a specific route in your Wails application. We'll build a complete example that demonstrates how to:
+
+1. Create a Gin-based service
+2. Implement the Wails Service interface
+3. Set up routes and middleware
+4. Integrate with the Wails event system
+5. Interact with the service from the frontend
+
+## Prerequisites
+
+Before you begin, make sure you have:
+
+- Wails v3 installed
+- Basic knowledge of Go and the Gin framework
+- Familiarity with HTTP concepts and RESTful APIs
+
+You'll need to add the Gin framework to your project:
+
+```bash
+go get github.com/gin-gonic/gin
+```
+
+## Creating a Gin-Based Service
+
+Let's start by creating a Gin service that implements the Wails Service interface. Our service will manage a collection of users and provide API endpoints for retrieving and creating user records.
+
+### 1. Define Your Data Models
+
+First, define the data structures your service will work with:
+
+```go
+package services
+
+import (
+	"context"
+	"net/http"
+	"strconv"
+	"sync"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/wailsapp/wails/v3/pkg/application"
+)
+
+// User represents a user in the system
+type User struct {
+	ID        int       `json:"id"`
+	Name      string    `json:"name"`
+	Email     string    `json:"email"`
+	CreatedAt time.Time `json:"createdAt"`
+}
+
+// EventData represents data sent in events
+type EventData struct {
+	Message   string `json:"message"`
+	Timestamp string `json:"timestamp"`
+}
+```
+
+### 2. Create Your Service Structure
+
+Next, define the service structure that will hold your Gin router and any state your service needs to maintain:
+
+```go
+// GinService implements a Wails service that uses Gin for HTTP handling
+type GinService struct {
+	ginEngine *gin.Engine
+	users     []User
+	nextID    int
+	mu        sync.RWMutex
+	app       *application.App
+}
+
+// NewGinService creates a new GinService instance
+func NewGinService() *GinService {
+	// Create a new Gin router
+	ginEngine := gin.New()
+
+	// Add middlewares
+	ginEngine.Use(gin.Recovery())
+	ginEngine.Use(LoggingMiddleware())
+
+	service := &GinService{
+		ginEngine: ginEngine,
+		users: []User{
+			{ID: 1, Name: "Alice", Email: "alice@example.com", CreatedAt: time.Now().Add(-72 * time.Hour)},
+			{ID: 2, Name: "Bob", Email: "bob@example.com", CreatedAt: time.Now().Add(-48 * time.Hour)},
+			{ID: 3, Name: "Charlie", Email: "charlie@example.com", CreatedAt: time.Now().Add(-24 * time.Hour)},
+		},
+		nextID: 4,
+	}
+
+	// Define routes
+	service.setupRoutes()
+
+	return service
+}
+```
+
+### 3. Implement the Service Interface
+
+Implement the required methods for the Wails Service interface:
+
+```go
+// ServiceName returns the name of the service
+func (s *GinService) ServiceName() string {
+	return "Gin API Service"
+}
+
+// ServiceStartup is called when the service starts
+func (s *GinService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
+	// Store the application instance for later use
+	s.app = application.Get()
+
+	// Register an event handler that can be triggered from the frontend
+	s.app.Event.On("gin-api-event", func(event *application.CustomEvent) {
+		// Log the event data
+		s.app.Logger.Info("Received event from frontend", "data", event.Data)
+
+		// Emit an event back to the frontend
+		s.app.Event.EmitEvent(&application.CustomEvent{
+			Name: "gin-api-response",
+			Data: map[string]interface{}{
+                "message": "Response from Gin API Service",
+                "time":    time.Now().Format(time.RFC3339),
+            },
+		})
+	})
+
+	return nil
+}
+
+// ServiceShutdown is called when the service shuts down
+func (s *GinService) ServiceShutdown(ctx context.Context) error {
+	// Clean up resources if needed
+	return nil
+}
+```
+
+### 3. Implement the http.Handler Interface
+
+To make your service mountable at a specific route, implement the `http.Handler` interface. This single method, `ServeHTTP`, is the gateway for all HTTP requests to your service. It delegates the request handling to the Gin router, allowing you to use all of Gin's powerful features for routing and middleware.
+
+```go
+// ServeHTTP implements the http.Handler interface
+func (s *GinService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// All requests go to the Gin router
+	s.ginEngine.ServeHTTP(w, r)
+}
+```
+
+### 4. Set Up Your Routes
+
+Define your API routes in a separate method for better organisation. This approach keeps your code clean and makes it easier to understand the structure of your API. The Gin router provides a fluent API for defining routes, including support for route groups, which help organise related endpoints.
+
+```go
+// setupRoutes configures the API routes
+func (s *GinService) setupRoutes() {
+	// Basic info endpoint
+	s.ginEngine.GET("/info", func(c *gin.Context) {
+		c.JSON(http.StatusOK, gin.H{
+			"service": "Gin API Service",
+			"version": "1.0.0",
+			"time":    time.Now().Format(time.RFC3339),
+		})
+	})
+
+	// Users group
+	users := s.ginEngine.Group("/users")
+	{
+		// Get all users
+		users.GET("", func(c *gin.Context) {
+			s.mu.RLock()
+			defer s.mu.RUnlock()
+			c.JSON(http.StatusOK, s.users)
+		})
+
+		// Get user by ID
+		users.GET("/:id", func(c *gin.Context) {
+			id, err := strconv.Atoi(c.Param("id"))
+			if err != nil {
+				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
+				return
+			}
+
+			s.mu.RLock()
+			defer s.mu.RUnlock()
+
+			for _, user := range s.users {
+				if user.ID == id {
+					c.JSON(http.StatusOK, user)
+					return
+				}
+			}
+
+			c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
+		})
+
+		// Create a new user
+		users.POST("", func(c *gin.Context) {
+			var newUser User
+			if err := c.ShouldBindJSON(&newUser); err != nil {
+				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+				return
+			}
+
+			s.mu.Lock()
+			defer s.mu.Unlock()
+
+			// Set the ID and creation time
+			newUser.ID = s.nextID
+			newUser.CreatedAt = time.Now()
+			s.nextID++
+
+			// Add to the users slice
+			s.users = append(s.users, newUser)
+
+			c.JSON(http.StatusCreated, newUser)
+
+			// Emit an event to notify about the new user
+			s.app.Event.Emit("user-created", newUser)
+		})
+
+		// Delete a user
+		users.DELETE("/:id", func(c *gin.Context) {
+			id, err := strconv.Atoi(c.Param("id"))
+			if err != nil {
+				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
+				return
+			}
+
+			s.mu.Lock()
+			defer s.mu.Unlock()
+
+			for i, user := range s.users {
+				if user.ID == id {
+					// Remove the user from the slice
+					s.users = append(s.users[:i], s.users[i+1:]...)
+					c.JSON(http.StatusOK, gin.H{"message": "User deleted"})
+					return
+				}
+			}
+
+			c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
+		})
+	}
+}
+```
+
+### 5. Create Custom Middleware
+
+You can create custom Gin middleware to enhance your service. Middleware functions in Gin are executed in the order they are added to the router and can perform tasks such as logging, authentication, and error handling. This example shows a simple logging middleware that records request details.
+
+```go
+// LoggingMiddleware is a Gin middleware that logs request details
+func LoggingMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		// Start timer
+		start := time.Now()
+
+		// Process request
+		c.Next()
+
+		// Calculate latency
+		latency := time.Since(start)
+
+		// Log request details
+		log.Printf("[GIN] %s %s %d %s", c.Request.Method, c.Request.URL.Path, c.Writer.Status(), latency)
+	}
+}
+```
+
+## Registering Your Service
+
+To use your Gin-based service in a Wails application, you need to register it with the application and specify the route where it should be mounted. This is done when creating the Wails application instance. The route you specify becomes the base path for all endpoints defined in your Gin router.
+
+```go
+app := application.New(application.Options{
+    Name:        "Gin Service Demo",
+    Description: "A demo of using Gin in Wails services",
+    Mac: application.MacOptions{
+        ApplicationShouldTerminateAfterLastWindowClosed: true,
+    },
+    LogLevel: slog.LevelDebug,
+    Services: []application.Service{
+        application.NewServiceWithOptions(services.NewGinService(), application.ServiceOptions{
+            Route: "/api",
+        }),
+    },
+    Assets: application.AssetOptions{
+        Handler: application.BundledAssetFileServer(assets),
+    },
+})
+```
+
+In this example, the Gin service is mounted at the `/api` route. This means that if your Gin router has an endpoint defined as `/info`, it will be accessible at `/api/info` in your application. This approach allows you to organise your API endpoints logically and avoid conflicts with other parts of your application.
+
+## Integrating with the Wails Event System
+
+One of the powerful features of using Gin with Wails Services is the ability to seamlessly integrate with the Wails event system. This allows for real-time communication between your backend service and the frontend.
+
+In your service's `ServiceStartup` method, you can register event handlers to process events from the frontend:
+
+```go
+s.app.Event.On("gin-api-event", func(event *application.CustomEvent) {
+	// Log the event data
+	s.app.Logger.Info("Received event from frontend", "data", event.Data)
+
+	// Emit an event back to the frontend
+	s.app.Event.EmitEvent(&application.CustomEvent{
+		Name: "gin-api-response",
+		Data: map[string]interface{}{
+			"message": "Response from Gin API Service",
+			"time":    time.Now().Format(time.RFC3339),
+		},
+	})
+})
+```
+
+You can also emit events to the frontend from your Gin routes or other parts of your service:
+
+```go
+// After creating a new user
+s.app.Event.Emit("user-created", newUser)
+```
+
+## Frontend Integration
+
+To interact with your Gin service from the frontend, you'll need to import the Wails runtime, make HTTP requests to your API endpoints, and use the Wails event system for real-time communication.
+
+For production use, it's recommended to use the `@wailsio/runtime` package instead of directly importing `/wails/runtime.js`. This ensures type safety, better IDE support, version management through npm, and compatibility with modern JavaScript tooling.
+
+Install the package:
+```bash
+npm install @wailsio/runtime
+```
+
+Then use it in your code:
+```javascript
+import * as wails from '@wailsio/runtime';
+
+// Event emission
+wails.Events.Emit({
+    name: 'gin-api-event',
+    data: eventData,
+});
+```
+
+Here's an example of how to set up frontend integration:
+
+```html
+
+
+
+    
+    
+    Gin Service Example
+    
+
+
+    

Gin Service Example

+ +
+

API Endpoints

+

Try the Gin API endpoints mounted at /api:

+ + + + + + + +
+
Results will appear here...
+
+
+ +
+

Event Communication

+

Trigger an event to communicate with the Gin service:

+ + + +
+
Event responses will appear here...
+
+
+ + + + + + +``` + +## Closing Thoughts + +Integrating the Gin web framework with Wails v3 Services provides a powerful and flexible approach to building modular, maintainable web applications. By leveraging Gin's routing and middleware capabilities alongside the Wails event system, you can create rich, interactive applications with clean separation of concerns. + +The complete example code for this guide can be found in the Wails repository under `v3/examples/gin-service`. diff --git a/docs/src/content/docs/guides/menus.mdx b/docs/src/content/docs/guides/menus.mdx new file mode 100644 index 000000000..c16c40ff6 --- /dev/null +++ b/docs/src/content/docs/guides/menus.mdx @@ -0,0 +1,531 @@ +--- +title: Menus +description: A guide to creating and customising menus in Wails v3 +--- + +Wails v3 provides a powerful menu system that allows you to create both application menus and context menus. This guide will walk you through the various features and capabilities of the menu system. + +### Creating a Menu + +To create a new menu, use the `New()` method from the Menus manager: + +```go +menu := app.Menu.New() +``` + +### Adding Menu Items + +Wails supports several types of menu items, each serving a specific purpose: + +#### Regular Menu Items +Regular menu items are the basic building blocks of menus. They display text and can trigger actions when clicked: + +```go +menuItem := menu.Add("Click Me") +``` + +#### Checkboxes +Checkbox menu items provide a toggleable state, useful for enabling/disabling features or settings: + +```go +checkbox := menu.AddCheckbox("My checkbox", true) // true = initially checked +``` + +#### Radio Groups +Radio groups allow users to select one option from a set of mutually exclusive choices. They are automatically created when radio items are placed next to each other: + +```go +menu.AddRadio("Option 1", true) // true = initially selected +menu.AddRadio("Option 2", false) +menu.AddRadio("Option 3", false) +``` + +#### Separators +Separators are horizontal lines that help organise menu items into logical groups: + +```go +menu.AddSeparator() +``` + +#### Submenus +Submenus are nested menus that appear when hovering over or clicking a menu item. They're useful for organizing complex menu structures: + +```go +submenu := menu.AddSubmenu("File") +submenu.Add("Open") +submenu.Add("Save") +``` + +#### Combining menus +A menu can be added into another menu by appending or prepending it. +```go +menu := app.Menu.New() +menu.Add("First Menu") + +secondaryMenu := app.Menu.New() +secondaryMenu.Add("Second Menu") + +// insert 'secondaryMenu' after 'menu' +menu.Append(secondaryMenu) + +// insert 'secondaryMenu' before 'menu' +menu.Prepend(secondaryMenu) + +// update the menu +menu.Update() +``` + +:::note +By default, `prepend` and `append` will share state with the original menu. If you want to create a new menu with its own state, +you can call `.Clone()` on the menu. + +E.g: `menu.Append(secondaryMenu.Clone())` +::: + +#### Clearing a menu +In some cases it'll be better to construct a whole new menu if you are working with a variable amount of menu items. + +This will clear all items on an existing menu and allows you to add items again. + +```go +menu := app.Menu.New() +menu.Add("Waiting for update...") + +// after certain logic, the menu has to be updated +menu.Clear() +menu.Add("Update complete!") +menu.Update() +``` + +:::note +Clearing a menu simply clears the menu items at the top level. Whilst Submenus won't be visible, they will still occupy memory +so be sure to manage your menus carefully. +::: + +#### Destroying a menu + +If you want to clear and release a menu, use the `Destroy()` method: + +```go +menu := app.Menu.New() +menu.Add("Waiting for update...") + +// after certain logic, the menu has to be destroyed +menu.Destroy() +``` + + +### Menu Item Properties + +Menu items have several properties that can be configured: + +| Property | Method | Description | +|-------------|--------------------------|-----------------------------------------------------| +| Label | `SetLabel(string)` | Sets the display text | +| Enabled | `SetEnabled(bool)` | Enables/disables the item | +| Checked | `SetChecked(bool)` | Sets the checked state (for checkboxes/radio items) | +| Tooltip | `SetTooltip(string)` | Sets the tooltip text | +| Hidden | `SetHidden(bool)` | Shows/hides the item | +| Accelerator | `SetAccelerator(string)` | Sets the keyboard shortcut | + +### Menu Item States + +Menu items can be in different states that control their visibility and interactivity: + +#### Visibility + +Menu items can be shown or hidden dynamically using the `SetHidden()` method: + +```go +menuItem := menu.Add("Dynamic Item") + +// Hide the menu item +menuItem.SetHidden(true) + +// Show the menu item +menuItem.SetHidden(false) + +// Check current visibility +isHidden := menuItem.Hidden() +``` + +Hidden menu items are completely removed from the menu until shown again. This is useful for contextual menu items that should only appear in certain application states. + +#### Enabled State + +Menu items can be enabled or disabled using the `SetEnabled()` method: + +```go +menuItem := menu.Add("Save") + +// Disable the menu item +menuItem.SetEnabled(false) // Item appears grayed out and cannot be clicked + +// Enable the menu item +menuItem.SetEnabled(true) // Item becomes clickable again + +// Check current enabled state +isEnabled := menuItem.Enabled() +``` + +Disabled menu items remain visible but appear grayed out and cannot be clicked. This is commonly used to indicate that an action is currently unavailable, such as: +- Disabling "Save" when there are no changes to save +- Disabling "Copy" when nothing is selected +- Disabling "Undo" when there's no action to undo + +#### Dynamic State Management + +You can combine these states with event handlers to create dynamic menus: + +```go +saveMenuItem := menu.Add("Save") + +// Initially disable the Save menu item +saveMenuItem.SetEnabled(false) + +// Enable Save only when there are unsaved changes +documentChanged := func() { + saveMenuItem.SetEnabled(true) + menu.Update() // Remember to update the menu after changing states +} + +// Disable Save after saving +documentSaved := func() { + saveMenuItem.SetEnabled(false) + menu.Update() +} +``` + +### Event Handling + +Menu items can respond to click events using the `OnClick` method: + +```go +menuItem.OnClick(func(ctx *application.Context) { + // Handle the click event + println("Menu item clicked!") +}) +``` + +The context provides information about the clicked menu item: + +```go +menuItem.OnClick(func(ctx *application.Context) { + // Get the clicked menu item + clickedItem := ctx.ClickedMenuItem() + // Get its current state + isChecked := clickedItem.Checked() +}) +``` + +### Role-Based Menu Items + +Wails provides a set of predefined menu roles that automatically create menu items with standard functionality. Here are the supported menu roles: + +#### Complete Menu Structures +These roles create entire menu structures with common functionality: + +| Role | Description | Platform Notes | +|------|-------------|----------------| +| `AppMenu` | Application menu with About, Services, Hide/Show, and Quit | macOS only | +| `EditMenu` | Standard Edit menu with Undo, Redo, Cut, Copy, Paste, etc. | All platforms | +| `ViewMenu` | View menu with Reload, Zoom, and Fullscreen controls | All platforms | +| `WindowMenu` | Window controls (Minimise, Zoom, etc.) | All platforms | +| `HelpMenu` | Help menu with "Learn More" link to Wails website | All platforms | + +#### Individual Menu Items +These roles can be used to add individual menu items: + +| Role | Description | Platform Notes | +|------|-------------|----------------| +| `About` | Show application About dialog | All platforms | +| `Hide` | Hide application | macOS only | +| `HideOthers` | Hide other applications | macOS only | +| `UnHide` | Show hidden application | macOS only | +| `CloseWindow` | Close current window | All platforms | +| `Minimise` | Minimise window | All platforms | +| `Zoom` | Zoom window | macOS only | +| `Front` | Bring window to front | macOS only | +| `Quit` | Quit application | All platforms | +| `Undo` | Undo last action | All platforms | +| `Redo` | Redo last action | All platforms | +| `Cut` | Cut selection | All platforms | +| `Copy` | Copy selection | All platforms | +| `Paste` | Paste from clipboard | All platforms | +| `PasteAndMatchStyle` | Paste and match style | macOS only | +| `SelectAll` | Select all | All platforms | +| `Delete` | Delete selection | All platforms | +| `Reload` | Reload current page | All platforms | +| `ForceReload` | Force reload current page | All platforms | +| `ToggleFullscreen` | Toggle fullscreen mode | All platforms | +| `ResetZoom` | Reset zoom level | All platforms | +| `ZoomIn` | Increase zoom | All platforms | +| `ZoomOut` | Decrease zoom | All platforms | + +Here's an example showing how to use both complete menus and individual roles: + +```go +menu := app.Menu.New() + +// Add complete menu structures +menu.AddRole(application.AppMenu) // macOS only +menu.AddRole(application.EditMenu) // Common edit operations +menu.AddRole(application.ViewMenu) // View controls +menu.AddRole(application.WindowMenu) // Window controls + +// Add individual role-based items to a custom menu +fileMenu := menu.AddSubmenu("File") +fileMenu.AddRole(application.CloseWindow) +fileMenu.AddSeparator() +fileMenu.AddRole(application.Quit) +``` + +## Application Menus + +Application menus are the menus that appear at the top of your application window (Windows/Linux) or at the top of the screen (macOS). + + +### Application Menu Behaviour + +When you set an application menu using `app.Menu.Set()`, it becomes the main menu on macOS. +Menus are set on a per-window basis for Windows/Linux. + +```go +app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Custom Menu Window", + Windows: application.WindowsWindow{ + Menu: customMenu, // Override application menu for this window + }, +}) +``` + +Here's a complete example showing these different menu behaviours: + +```go +func main() { + app := application.New(application.Options{}) + + // Create application menu + appMenu := app.Menu.New() + fileMenu := appMenu.AddSubmenu("File") + fileMenu.Add("New").OnClick(func(ctx *application.Context) { + // This will be available in all windows unless overridden + window := app.Windows.Current() + window.SetTitle("New Window") + }) + + // Set as application menu - this is for macOS + app.Menu.Set(appMenu) + + // Window with custom menu on Windows + customMenu := app.Menu.New() + customMenu.Add("Custom Action") + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Custom Menu", + Windows: application.WindowsWindow{ + Menu: customMenu, + }, + }) + + app.Run() +} +``` + +## Context Menus + +Context menus are popup menus that appear when right-clicking elements in your application. They provide quick access to relevant actions for the clicked element. + +### Default Context Menu + +The default context menu is the webview's built-in context menu that provides system-level operations such as: +- Copy, Cut, and Paste for text manipulation +- Text selection controls +- Spell checking options + +#### Controlling the Default Context Menu + +You can control when the default context menu appears using the `--default-contextmenu` CSS property: + +```html + +
+ + +
+ + +
+
Custom context menu only
+
+ + +
+ +

Select this text to see the default menu

+ +
+``` + +:::note +This feature will only work as expected after the runtime [has been initialised](../../learn/runtime#initialisation). +::: + +#### Nested Context Menu Behavior + +When using the `--default-contextmenu` property on nested elements, the following rules apply: + +1. Child elements inherit their parent's context menu setting unless explicitly overridden +2. The most specific (closest) setting takes precedence +3. The `auto` value can be used to reset to default behaviour + +Example of nested context menu behaviour: + +```html + +
+ +

No context menu here

+ + +
+

Context menu shown here

+ + + Also has context menu + + +
+

Shows menu only when text is selected

+
+
+
+``` + +### Custom Context Menus + +Custom context menus allow you to provide application-specific actions that are relevant to the element being clicked. They're particularly useful for: +- File operations in a document manager +- Image manipulation tools +- Custom actions in a data grid +- Component-specific operations + +#### Creating a Custom Context Menu + +When creating a custom context menu, you provide a unique identifier (name) that links the menu to HTML elements: + +```go +// Create a context menu with identifier "imageMenu" +contextMenu := application.NewContextMenu() +app.ContextMenus.Add("imageMenu", contextMenu) +``` + +The name parameter ("imageMenu" in this example) serves as a unique identifier that will be used to: +1. Link HTML elements to this specific context menu +2. Identify which menu should be shown when right-clicking +3. Allow menu updates and cleanup + +#### Context Data + +When handling context menu events, you can access both the clicked menu item and its associated context data: + +```go +contextMenu.Add("Process").OnClick(func(ctx *application.Context) { + // Get the clicked menu item + menuItem := ctx.ClickedMenuItem() + + // Get the context data as a string + contextData := ctx.ContextMenuData() + + // Check if the menu item is checked (for checkbox/radio items) + isChecked := ctx.IsChecked() + + // Use the data + if contextData != "" { + processItem(contextData) + } +}) +``` + +The context data is passed from the HTML element's `--custom-contextmenu-data` property and is available in the click handler through `ctx.ContextMenuData()`. This is particularly useful when: + +- Working with lists or grids where each item needs unique identification +- Handling operations on specific components or elements +- Passing state or metadata from the frontend to the backend + +#### Context Menu Management + +After making changes to a context menu, call the `Update()` method to apply the changes: + +```go +contextMenu.Update() +``` + +When you no longer need a context menu, you can destroy it: + +```go +contextMenu.Destroy() +``` +:::danger[Warning] +After calling `Destroy()`, using the context menu reference again will result in a panic. +::: + +### Real-World Example: Image Gallery + +Here's a complete example of implementing a custom context menu for an image gallery: + +```go +// Backend: Create the context menu +imageMenu := application.NewContextMenu() +app.ContextMenus.Add("imageMenu", imageMenu) + +// Add relevant operations +imageMenu.Add("View Full Size").OnClick(func(ctx *application.Context) { + // Get the image ID from context data + if imageID := ctx.ContextMenuData(); imageID != "" { + openFullSizeImage(imageID) + } +}) + +imageMenu.Add("Download").OnClick(func(ctx *application.Context) { + if imageID := ctx.ContextMenuData(); imageID != "" { + downloadImage(imageID) + } +}) + +imageMenu.Add("Share").OnClick(func(ctx *application.Context) { + if imageID := ctx.ContextMenuData(); imageID != "" { + showShareDialog(imageID) + } +}) +``` + +```html + + +``` + +In this example: +1. The context menu is created with the identifier "imageMenu" +2. Each image container is linked to the menu using `--custom-contextmenu: imageMenu` +3. Each container provides its image ID as context data using `--custom-contextmenu-data` +4. The backend receives the image ID in click handlers and can perform specific operations +5. The same menu is reused for all images, but the context data tells us which image to operate on + +This pattern is particularly powerful for: +- Data grids where rows need specific operations +- File managers where files need context-specific actions +- Design tools where different elements need different operations +- Any component where the same operations apply to multiple instances diff --git a/docs/src/content/docs/guides/msix-packaging.mdx b/docs/src/content/docs/guides/msix-packaging.mdx new file mode 100644 index 000000000..8b9ac57a1 --- /dev/null +++ b/docs/src/content/docs/guides/msix-packaging.mdx @@ -0,0 +1,138 @@ +--- +title: MSIX Packaging (Windows) +description: Guide for creating MSIX packages with Wails v3 +--- + +# MSIX Packaging (Windows) + +Wails v3 can generate modern **MSIX** installers for Windows applications, providing a cleaner, safer and Store-ready alternative to traditional **NSIS** or plain `.exe` bundles. + +This guide walks through: + +* Prerequisites & tool installation +* Building your app as an MSIX package +* Signing the package +* Command-line reference +* Troubleshooting + +--- + +## 1. Prerequisites + +| Requirement | Notes | +|-------------|-------| +| **Windows 10 1809+ / Windows 11** | MSIX is only supported on Windows. | +| **Windows SDK** (for `MakeAppx.exe` & `signtool.exe`) | Install from the [Windows SDK download page](https://developer.microsoft.com/windows/downloads/windows-sdk/). | +| **Microsoft MSIX Packaging Tool** (optional) | Available from the Microsoft Store – provides a GUI & CLI. | +| **Code-signing certificate** (recommended) | A `.pfx` file generated by your CA or `New-SelfSignedCertificate`. | + +> **Tip:** Wails ships a Task that opens the download pages for you: + +```bash +# installs MakeAppx / signtool (via Windows SDK) and the MSIX Packaging Tool +wails3 task windows:install:msix:tools +``` + +--- + +## 2. Building an MSIX package + +### 2.1 Quick CLI + +```bash +# Production build + MSIX +wails3 tool msix \ + --name MyApp \ # executable name + --executable build/bin/MyApp.exe \ + --arch x64 \ # x64, x86 or arm64 + --out build/bin/MyApp-x64.msix +``` + +The command will: + +1. Create a temporary layout (`AppxManifest.xml`, `Assets/`). +2. Call **MakeAppx.exe** (default) or **MsixPackagingTool.exe** if `--use-msix-tool` is passed. +3. Optionally sign the package (see §3). + +### 2.2 Using the generated Taskfile + +When you ran `wails init`, Wails created `build/windows/Taskfile.yml`. +Packaging with MSIX is a one-liner: + +```bash +# default=nsis, override FORMAT +wails3 task windows:package FORMAT=msix +``` + +Output goes to `build/bin/MyApp-.msix`. + +--- + +## 3. Signing the package + +Windows will refuse unsigned MSIX packages unless you enable developer-mode, so signing is strongly recommended. + +```bash +wails3 tool msix \ + --cert build/cert/CodeSign.pfx \ + --cert-password "pfx-password" \ + --publisher "CN=MyCompany" \ + --out build/bin/MyApp.msix +``` + +* If you pass `--cert`, Wails automatically runs `signtool sign …`. +* `--publisher` sets the `Publisher` field inside `AppxManifest.xml`. + It **must** match the subject of your certificate. + +--- + +## 4. Command-line reference + +| Flag | Default | Description | +|------|---------|-------------| +| `--config` | `wails.json` | Project config with **Info** & `fileAssociations`. | +| `--name` | — | Executable name inside the package (no spaces). | +| `--executable` | — | Path to the built `.exe`. | +| `--arch` | `x64` | `x64`, `x86`, or `arm64`. | +| `--out` | `.msix` | Output path / filename. | +| `--publisher` | `CN=` | Publisher string in the manifest. | +| `--cert` | ― | Path to `.pfx` certificate for signing. | +| `--cert-password` | ― | Password for the `.pfx`. | +| `--use-msix-tool` | `false` | Use **MsixPackagingTool.exe** instead of **MakeAppx.exe**. | +| `--use-makeappx` | `true` | Force MakeAppx even if the MSIX Tool is installed. | + +--- + +## 5. File associations + +Wails automatically injects file associations declared in `wails.json` into the package manifest: + +```json +"fileAssociations": [ + { "ext": "wails", "name": "Wails Project", "description": "Wails file", "role": "Editor" } +] +``` + +After installation, Windows will offer your app as a handler for these extensions. + +--- + +## 6. Troubleshooting + +| Problem | Solution | +|---------|----------| +| `MakeAppx.exe not found` | Install the Windows SDK and restart the terminal. | +| `signtool.exe not found` | Same as above – both live in the SDK’s *bin* folder. | +| *Package cannot be installed because publisher mismatch* | The certificate subject (CN) must match `--publisher`. | +| *The certificate is not trusted* | Import the certificate into **Trusted Root Certification Authorities** or use a publicly trusted code-signing cert. | +| Need GUI | Install **MSIX Packaging Tool** from the store and run `MsixPackagingTool.exe`. The template generated by Wails is fully compatible. | + +--- + +## 7. Next steps + +* [Windows Installer (NSIS) guide](./windows-installer.mdx) – legacy format. +* [Cross-platform update mechanism](../updates.mdx) – coming soon. +* Join the community on Discord to share feedback! + +Happy packaging! diff --git a/docs/src/content/docs/guides/panic-handling.mdx b/docs/src/content/docs/guides/panic-handling.mdx new file mode 100644 index 000000000..f252f0dc3 --- /dev/null +++ b/docs/src/content/docs/guides/panic-handling.mdx @@ -0,0 +1,116 @@ +--- +title: Handling Panics +description: How to handle panics in your Wails application +--- + +In Go applications, panics can occur during runtime when something unexpected happens. This guide explains how to handle panics both in general Go code and specifically in your Wails application. + +## Understanding Panics in Go + +Before diving into Wails-specific panic handling, it's essential to understand how panics work in Go: + +1. Panics are for unrecoverable errors that shouldn't happen during normal operation +2. When a panic occurs in a goroutine, only that goroutine is affected +3. Panics can be recovered using `defer` and `recover()` + +Here's a basic example of panic handling in Go: + +```go +func doSomething() { + // Deferred functions run even when a panic occurs + defer func() { + if r := recover(); r != nil { + fmt.Printf("Recovered from panic: %v\n", r) + } + }() + + // Your code that might panic + panic("something went wrong") +} +``` + +For more detailed information about panic and recover in Go, see the [Go Blog: Defer, Panic, and Recover](https://go.dev/blog/defer-panic-and-recover). + +## Panic Handling in Wails + +Wails automatically handles panics that occur in your Service methods when they are called from the frontend. This means you don't need to add panic recovery to these methods - Wails will catch the panic and process it through your configured panic handler. + +The panic handler is specifically designed to catch: +- Panics in bound service methods called from the frontend +- Internal panics from the Wails runtime + +For other scenarios, such as background goroutines or standalone Go code, you should handle panics yourself using Go's standard panic recovery mechanisms. + +## The PanicDetails Struct + +When a panic occurs, Wails captures important information about the panic in a `PanicDetails` struct: + +```go +type PanicDetails struct { + StackTrace string // The stack trace of where the panic occurred. Potentially trimmed to provide more context + Error error // The error that caused the panic + Time time.Time // The time when the panic occurred + FullStackTrace string // The complete stack trace including runtime frames +} +``` + +This structure provides comprehensive information about the panic: +- `StackTrace`: A formatted string showing the call stack that led to the panic +- `Error`: The actual error or panic message +- `Time`: The exact time when the panic occurred +- `FullStackTrace`: The complete stack trace including runtime frames + +:::note[Panics in Service Code] + +When panics are caught in your Service code after being called from the frontend, the stack trace is trimmed to focus on exactly where in your code the panic occurred. +If you want to see the full stack trace, you can use the `FullStackTrace` field. + +::: + +## Default Panic Handler + +If you don't specify a custom panic handler, Wails will use its default handler which outputs error information in a formatted log message and then quits. +For example: + +``` +************************ FATAL ****************************** +* There has been a catastrophic failure in your application * +********************* Error Details ************************* +panic error: oh no! something went wrong deep in my service! :( +main.(*WindowService).call2 + at E:/wails/v3/examples/panic-handling/main.go:23 +main.(*WindowService).call1 + at E:/wails/v3/examples/panic-handling/main.go:19 +main.(*WindowService).GeneratePanic + at E:/wails/v3/examples/panic-handling/main.go:15 +************************************************************* +``` + +## Custom Panic Handler + +You can implement your own panic handler by setting the `PanicHandler` option when creating your application. Here's an example: + +```go +app := application.New(application.Options{ + Name: "My App", + PanicHandler: func(panicDetails *application.PanicDetails) { + fmt.Printf("*** Custom Panic Handler ***\n") + fmt.Printf("Time: %s\n", panicDetails.Time) + fmt.Printf("Error: %s\n", panicDetails.Error) + fmt.Printf("Stacktrace: %s\n", panicDetails.StackTrace) + fmt.Printf("Full Stacktrace: %s\n", panicDetails.FullStackTrace) + + // You could also: + // - Log to a file + // - Send to a crash reporting service + // - Show a user-friendly error dialog + // - Attempt to recover or restart the application + }, +}) +``` + +For a complete working example of panic handling in a Wails application, see the panic-handling example in `v3/examples/panic-handling`. + +## Final Notes + +Remember that the Wails panic handler is specifically for managing panics in bound methods and internal runtime errors. For other parts of your application, you should use Go's standard error handling patterns and panic recovery mechanisms where appropriate. As with all Go applications, it's better to prevent panics through proper error handling where possible. diff --git a/docs/src/content/docs/guides/signing.mdx b/docs/src/content/docs/guides/signing.mdx new file mode 100644 index 000000000..b5de768c9 --- /dev/null +++ b/docs/src/content/docs/guides/signing.mdx @@ -0,0 +1,228 @@ +--- +title: Code Signing +description: Guide for signing your Wails applications on macOS and Windows +sidebar: + order: 4 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { Steps } from '@astrojs/starlight/components'; +import { Card, CardGrid } from '@astrojs/starlight/components'; + +# Code Signing Your Application + +This guide covers how to sign your Wails applications for both macOS and Windows, with a focus on automated signing using GitHub Actions. + + + + Sign your Windows executables with certificates + + + Sign and notarize your macOS applications + + + +## Windows Code Signing + + +1. **Obtain a Code Signing Certificate** + - Get from a trusted provider listed on [Microsoft's documentation](https://docs.microsoft.com/en-us/windows-hardware/drivers/dashboard/get-a-code-signing-certificate) + - Standard code signing certificate is sufficient (EV not required) + - Test signing locally before setting up CI + +2. **Prepare for GitHub Actions** + - Convert your certificate to Base64 + - Store in GitHub Secrets + - Set up signing workflow + +3. **Configure GitHub Actions** + ```yaml + name: Sign Windows Binary + + on: + workflow_dispatch: + release: + types: [created] + + jobs: + sign: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Import Certificate + run: | + New-Item -ItemType directory -Path certificate + Set-Content -Path certificate\certificate.txt -Value ${{ secrets.WINDOWS_CERTIFICATE }} + certutil -decode certificate\certificate.txt certificate\certificate.pfx + + - name: Sign Binary + run: | + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86\signtool.exe' sign /f certificate\certificate.pfx /t http://timestamp.sectigo.com /p ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} /v /fd sha256 .\build\bin\app.exe + ``` + + +### Important Windows Parameters + +- **Signing Algorithm**: Usually `sha256` +- **Timestamp Server**: Valid timestamping server URL +- **Certificate Password**: Stored in GitHub Secrets +- **Binary Path**: Path to your compiled executable + +## macOS Code Signing + + +1. **Prerequisites** + - Apple Developer Account + - Developer ID Certificate + - App Store Connect API Key + - [gon](https://github.com/mitchellh/gon) for notarization + +2. **Certificate Setup** + - Generate Developer ID Certificate + - Download and install certificate + - Export certificate for CI + +3. **Configure Notarization** + ```json title="gon-sign.json" + { + "source": ["./build/bin/app"], + "bundle_id": "com.company.app", + "apple_id": { + "username": "dev@company.com", + "password": "@env:AC_PASSWORD" + }, + "sign": { + "application_identity": "Developer ID Application: Company Name" + } + } + ``` + +4. **GitHub Actions Configuration** + ```yaml + name: Sign macOS Binary + + on: + workflow_dispatch: + release: + types: [created] + + jobs: + sign: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - name: Import Certificate + env: + MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} + run: | + echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 + security create-keychain -p "" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "" build.keychain + security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain + + - name: Sign and Notarize + env: + AC_USERNAME: ${{ secrets.AC_USERNAME }} + AC_PASSWORD: ${{ secrets.AC_PASSWORD }} + run: | + gon -log-level=info ./build/darwin/gon-sign.json + ``` + + +### Important macOS Parameters + +- **Bundle ID**: Unique identifier for your app +- **Developer ID**: Your Developer ID Application certificate +- **Apple ID**: Developer account credentials +- **ASC API Key**: App Store Connect API credentials + +## Best Practices + +1. **Security** + - Store all credentials in GitHub Secrets + - Use environment variables for sensitive data + - Regularly rotate certificates and credentials + +2. **Workflow** + - Test signing locally first + - Use conditional signing based on platform + - Implement proper error handling + +3. **Verification** + - Verify signatures after signing + - Test notarization process + - Check timestamp validity + +## Troubleshooting + +### Windows Issues +- Certificate not found +- Invalid timestamp server +- Signing tool errors + +### macOS Issues +- Keychain access issues +- Notarization failures +- Certificate validation errors + +## Complete GitHub Actions Workflow + +```yaml +name: Sign Binaries + +on: + workflow_dispatch: + release: + types: [created] + +jobs: + sign: + strategy: + matrix: + platform: [windows-latest, macos-latest] + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v3 + + # Windows Signing + - name: Sign Windows Binary + if: matrix.platform == 'windows-latest' + env: + CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} + CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} + run: | + New-Item -ItemType directory -Path certificate + Set-Content -Path certificate\certificate.txt -Value $env:CERTIFICATE + certutil -decode certificate\certificate.txt certificate\certificate.pfx + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86\signtool.exe' sign /f certificate\certificate.pfx /t http://timestamp.sectigo.com /p $env:CERTIFICATE_PASSWORD /v /fd sha256 .\build\bin\app.exe + + # macOS Signing + - name: Sign macOS Binary + if: matrix.platform == 'macos-latest' + env: + MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} + AC_USERNAME: ${{ secrets.AC_USERNAME }} + AC_PASSWORD: ${{ secrets.AC_PASSWORD }} + run: | + echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 + security create-keychain -p "" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "" build.keychain + security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain + gon -log-level=info ./build/darwin/gon-sign.json +``` + +## Additional Resources + +- [Apple Code Signing Documentation](https://developer.apple.com/support/code-signing/) +- [Microsoft Code Signing Documentation](https://docs.microsoft.com/en-us/windows-hardware/drivers/dashboard/get-a-code-signing-certificate) +- [Gon Documentation](https://github.com/mitchellh/gon) +- [GitHub Actions Documentation](https://docs.github.com/en/actions) diff --git a/docs/src/content/docs/guides/single-instance.mdx b/docs/src/content/docs/guides/single-instance.mdx new file mode 100644 index 000000000..25b7fac47 --- /dev/null +++ b/docs/src/content/docs/guides/single-instance.mdx @@ -0,0 +1,119 @@ +--- +title: Single Instance +description: Limiting your app to a single running instance +sidebar: + order: 40 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Single instance locking is a mechanism that prevents multiple instances of your app from running at the same time. +It is useful for apps that are designed to open files from the command line or from the OS file explorer. + + +## Usage + +To enable single instance functionality in your app, provide a `SingleInstanceOptions` struct when creating your application: + +```go +app := application.New(application.Options{ + // ... other options ... + SingleInstance: &application.SingleInstanceOptions{ + UniqueID: "com.myapp.unique-id", + OnSecondInstanceLaunch: func(data application.SecondInstanceData) { + log.Printf("Second instance launched with args: %v", data.Args) + log.Printf("Working directory: %s", data.WorkingDir) + log.Printf("Additional data: %v", data.AdditionalData) + }, + // Optional: Pass additional data to second instance + AdditionalData: map[string]string{ + "launchtime": time.Now().String(), + }, + }, +}) +``` + +The `SingleInstanceOptions` struct has the following fields: + +- `UniqueID`: A unique identifier for your application. This should be a unique string, typically in reverse domain notation (e.g., "com.company.appname"). +- `EncryptionKey`: Optional 32-byte array for encrypting data passed between instances using AES-256-GCM. If provided as a non-zero array, all communication between instances will be encrypted. +- `OnSecondInstanceLaunch`: A callback function that is called when a second instance of your app is launched. The callback receives a `SecondInstanceData` struct containing: + - `Args`: The command line arguments passed to the second instance + - `WorkingDir`: The working directory of the second instance + - `AdditionalData`: Any additional data passed from the second instance (if provided) +- `AdditionalData`: Optional map of string key-value pairs that will be passed to the first instance when subsequent instances are launched + +:::danger[Warning] +The Single Instance feature implements an optional encryption protocol using AES-256-GCM. Without encryption enabled, +data passed between instances is not secure. When using the single instance feature without encryption, +your app should treat any data passed to it from second instance callback as untrusted. +You should verify that args that you receive are valid and don't contain any malicious data. +::: + +### Secure Communication + +To enable secure communication between instances, provide a 32-byte encryption key. This key must be the same for all instances of your application: + +```go +// Define your encryption key (must be exactly 32 bytes) +var encryptionKey = [32]byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, +} + +// Use the key in SingleInstanceOptions +SingleInstance: &application.SingleInstanceOptions{ + UniqueID: "com.myapp.unique-id", + // Enable encryption for instance communication + EncryptionKey: encryptionKey, + // ... other options ... +} +``` + +:::tip[Security Best Practices] +- Use a unique key for your application +- Store the key securely if loading it from configuration +- Do not use the example key shown above - create your own! +::: + +### Window Management + +When handling second instance launches, you'll often want to bring your application window to the front. You can do this using the window's `Focus()` method. If your window is minimized, you may need to restore it first: + +```go + + var mainWindow *application.WebviewWindow + + SingleInstance: &application.SingleInstanceOptions{ + // Other options... + OnSecondInstanceLaunch: func(data application.SecondInstanceData) { + // Focus the window if needed + if mainWindow != nil { + mainWindow.Restore() + mainWindow.Focus() + } + }, + } +``` + +## How it works + + + + + Single instance lock using a named mutex. The mutex name is generated from the unique id that you provide. Data is passed to the first instance via [NSDistributedNotificationCenter](https://developer.apple.com/documentation/foundation/nsdistributednotificationcenter) + + + + + Single instance lock using a named mutex. The mutex name is generated from the unique id that you provide. Data is passed to the first instance via a shared window using [SendMessage](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage) + + + + + Single instance lock using [dbus](https://www.freedesktop.org/wiki/Software/dbus/). The dbus name is generated from the unique id that you provide. Data is passed to the first instance via [dbus](https://www.freedesktop.org/wiki/Software/dbus/) + + + diff --git a/docs/src/content/docs/guides/windows-uac.mdx b/docs/src/content/docs/guides/windows-uac.mdx new file mode 100644 index 000000000..5bb380179 --- /dev/null +++ b/docs/src/content/docs/guides/windows-uac.mdx @@ -0,0 +1,151 @@ +--- +title: Windows UAC Configuration +sidebar: + order: 11 +--- + +import {Badge} from '@astrojs/starlight/components'; + +Relevant Platforms: +
+ +Windows User Account Control (UAC) determines the execution privileges of your Wails application. By default, Wails v3 applications include explicit UAC configuration in their Windows manifest, ensuring consistent behavior across different machines. + +## UAC Execution Levels + +Windows applications can request different execution levels through their manifest file. Wails v3 automatically includes UAC configuration with a default execution level that you can customize based on your application's needs. + +### Available Execution Levels + +| Level | Description | Use Case | +|-------|-------------|----------| +| `asInvoker` | Runs with the same privileges as the parent process | Default for most applications | +| `highestAvailable` | Runs with the highest privileges available to the user | Applications that may need elevated access | +| `requireAdministrator` | Always requires administrator privileges | System utilities, installers | + +### Default Configuration + +Wails v3 applications include a default UAC configuration in their Windows manifest: + +```xml + + + + + + + +``` + +This configuration ensures your application: +- Runs with the same privileges as the launching process +- Does not require elevation by default +- Works consistently across different machines +- Does not trigger UAC prompts for normal users + +## Customizing UAC Configuration + +Since Wails v3 encourages users to customize their build assets, you can modify the UAC configuration by editing your Windows manifest template directly. + +### Locating the Manifest Template + +The Windows manifest template is located at: +``` +build/windows/wails.exe.manifest +``` + +### Modifying the Execution Level + +To change the execution level, edit the `level` attribute in the `requestedExecutionLevel` element: + +```xml title="build/windows/wails.exe.manifest" + + + + + + + +``` + +### Examples + +#### Standard Application (Default) +Most applications should use the default `asInvoker` level: + +```xml + +``` + +#### System Utility +Applications that need elevated access when available: + +```xml + +``` + +#### Administrative Tool +Applications that always require administrator privileges: + +```xml + +``` + +## UI Access + +The `uiAccess` attribute controls whether your application can interact with higher-privilege UI elements. In most cases, this should remain `false`. + +Set to `true` only if your application needs to: +- Send input to other applications +- Drive the UI of other applications +- Access UI elements of higher-privilege processes + +:::caution[UI Access Requirements] +Setting `uiAccess="true"` requires your application to be: +- Digitally signed with a certificate from a trusted certificate authority +- Installed in a secure location (Program Files or Windows\System32) +::: + +## Building with Custom UAC Settings + +After modifying your manifest template, build your application normally: + +```bash +wails3 build +``` + +The build process will automatically embed your custom UAC configuration into the executable. + +## Verifying UAC Configuration + +You can verify that your UAC settings are properly embedded using the `go-winres` tool: + +```bash +go-winres extract --in your-app.exe --out extracted-resources/ +``` + +Then examine the extracted manifest file to confirm your UAC configuration is present. + +:::tip[Manifest Persistence] +Unlike some other frameworks, Wails v3's UAC configuration is embedded directly into the executable during compilation, ensuring it persists when the application is copied to other machines. +::: + +## Troubleshooting + +### UAC Prompts Not Appearing +If you set `requireAdministrator` but don't see UAC prompts: +- Verify the manifest is properly embedded in your executable +- Check that you're not running from an already-elevated process +- Ensure the manifest syntax is valid XML + +### Application Not Starting +If your application fails to start after UAC changes: +- Check the manifest syntax for XML errors +- Verify the execution level value is valid +- Try reverting to `asInvoker` to isolate the issue + +### Inconsistent Behavior Across Machines +If UAC behavior differs between machines: +- Ensure the manifest is embedded in the executable (not external) +- Check that the executable wasn't modified after building +- Verify Windows UAC settings are enabled on the target machine \ No newline at end of file diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx new file mode 100644 index 000000000..3b71e86be --- /dev/null +++ b/docs/src/content/docs/index.mdx @@ -0,0 +1,114 @@ +--- +title: Welcome to Wails +description: "Create beautiful applications using Go" +banner: + content: | + This site is for v3 Alpha and is under construction. v2 is the stable release. +template: splash +hero: + tagline: + A powerful framework for building desktop applications using Go and modern web + technologies. + image: + dark: ../../assets/wails-logo-dark.svg + light: ../../assets/wails-logo-light.svg + alt: Wails Logo + + actions: + - text: Getting Started + link: /getting-started/installation + icon: right-arrow + - text: Sponsor + link: https://github.com/sponsors/leaanthony + icon: heart + class: + - is-sponsor +--- + +import { Card, CardGrid } from "@astrojs/starlight/components"; +import CardAnimation from '../../components/CardAnimation.astro'; + + :::danger[Alpha Status] + Wails v3 is currently in ALPHA. Please follow our [feedback guide](/feedback) to help us improve the project. + This site is still under construction. + ::: + +
+ +
+ + + Build desktop applications that combine the power of Go with modern web technologies: + + - **Native Performance**: Direct in-memory Go-to-Frontend communication + - **Cross Platform**: One codebase for Windows, macOS and Linux + - **Modern Stack**: Use any modern web framework (React, Vue, Svelte, etc.) + - **Native APIs**: Access OS-level features directly from Go + - **Developer Experience**: Hot reload, native dialogs, system tray, and more + - **Small Footprint**: Lightweight alternative to Electron + + + + Before you begin, ensure you have: + + - Go 1.23 or later installed + - Latest version of npm installed (if you want to use the templates) + - Basic knowledge of Go programming + + Check our [detailed prerequisites](/getting-started/installation#dependencies) for a complete list. + + + + ```bash + # Install wails + go install github.com/wailsapp/wails/v3/cmd/wails3@latest + + # Create a new project + wails3 init -n myproject + + # Run your project + cd myproject + wails3 dev + ``` + + + + Wails v3 is currently in Alpha. While it's stable enough for testing and + development, there might be breaking changes before the final release. Your feedback + and contributions are welcome! + + + + - **Multiple Windows Support**: Create and manage multiple windows in a single application + - **Improved API Design**: New procedural approach for more flexibility + - **Enhanced Bindings**: Sophisticated static analyzer for Go-to-Frontend communication + - **Better Build Tooling**: A new build system based on [Taskfile](https://taskfile.dev/) + - **New Linux Packaging**: Support for deb, rpm, arch linux, and AppImage + - **New Templates**: Create applications with a single command using our pre-built templates + + + + Ready to build your first Wails application? Check out our + [installation guide](/getting-started/installation) to get started. + + + + We value your feedback and have a [detailed guide on providing it](/feedback). + Use this guide to: + + - Report bugs + - Request features + - Get help from the community + - Contribute to the project + + Join our [Discord community](https://discord.gg/bdj28QNHmT) + or visit our [GitHub repository](https://github.com/wailsapp/wails) to: + + + + Please note that v3 is currently in Alpha. While we're working hard to ensure + stability, there might be breaking changes before the final release. For production + use, please use [Wails v2](https://wails.io). + + +
diff --git a/docs/src/content/docs/learn/advanced-binding.mdx b/docs/src/content/docs/learn/advanced-binding.mdx new file mode 100644 index 000000000..82fbb7dec --- /dev/null +++ b/docs/src/content/docs/learn/advanced-binding.mdx @@ -0,0 +1,342 @@ +--- +title: Advanced Binding Techniques +sidebar: + order: 22 +--- + +import { FileTree } from "@astrojs/starlight/components"; + +This guide covers advanced techniques for customizing and optimizing the binding generation process in Wails v3. + +## Customizing Generated Code with Directives + +### Injecting Custom Code + +The `//wails:inject` directive allows you to inject custom JavaScript/TypeScript code into the generated bindings: + +```go +//wails:inject console.log("Hello from Wails!"); +type MyService struct {} + +func (s *MyService) Greet(name string) string { + return "Hello, " + name +} +``` + +This will inject the specified code into the generated JavaScript/TypeScript file for the `MyService` service. + +You can also use conditional injection to target specific output formats: + +```go +//wails:inject j*:console.log("Hello JS!"); // JavaScript only +//wails:inject t*:console.log("Hello TS!"); // TypeScript only +``` + +### Including Additional Files + +The `//wails:include` directive allows you to include additional files with the generated bindings: + +```go +//wails:include js/*.js +package mypackage +``` + +This directive is typically used in package documentation comments to include additional JavaScript/TypeScript files with the generated bindings. + +### Marking Internal Types and Methods + +The `//wails:internal` directive marks a type or method as internal, preventing it from being exported to the frontend: + +```go +//wails:internal +type InternalModel struct { + Field string +} + +//wails:internal +func (s *MyService) InternalMethod() {} +``` + +This is useful for types and methods that are only used internally by your Go code and should not be exposed to the frontend. + +### Ignoring Methods + +The `//wails:ignore` directive completely ignores a method during binding generation: + +```go +//wails:ignore +func (s *MyService) IgnoredMethod() {} +``` + +This is similar to `//wails:internal`, but it completely ignores the method rather than marking it as internal. + +### Custom Method IDs + +The `//wails:id` directive specifies a custom ID for a method, overriding the default hash-based ID: + +```go +//wails:id 42 +func (s *MyService) CustomIDMethod() {} +``` + +This can be useful for maintaining compatibility when refactoring code. + +## Working with Complex Types + +### Nested Structs + +The binding generator handles nested structs automatically: + +```go +type Address struct { + Street string + City string + State string + Zip string +} + +type Person struct { + Name string + Address Address +} + +func (s *MyService) GetPerson() Person { + return Person{ + Name: "John Doe", + Address: Address{ + Street: "123 Main St", + City: "Anytown", + State: "CA", + Zip: "12345", + }, + } +} +``` + +The generated JavaScript/TypeScript code will include classes for both `Person` and `Address`. + +### Maps and Slices + +Maps and slices are also handled automatically: + +```go +type Person struct { + Name string + Attributes map[string]string + Friends []string +} + +func (s *MyService) GetPerson() Person { + return Person{ + Name: "John Doe", + Attributes: map[string]string{ + "hair": "brown", + "eyes": "blue", + }, + Friends: []string{"Jane", "Bob", "Alice"}, + } +} +``` + +In JavaScript, maps are represented as objects and slices as arrays. In TypeScript, maps are represented as `Record` and slices as `T[]`. + +### Generic Types + +The binding generator supports generic types: + +```go +type Result[T any] struct { + Data T + Error string +} + +func (s *MyService) GetResult() Result[string] { + return Result[string]{ + Data: "Hello, World!", + Error: "", + } +} +``` + +The generated TypeScript code will include a generic class for `Result`: + +```typescript +export class Result { + "Data": T; + "Error": string; + + constructor(source: Partial> = {}) { + if (!("Data" in source)) { + this["Data"] = null as any; + } + if (!("Error" in source)) { + this["Error"] = ""; + } + + Object.assign(this, source); + } + + static createFrom(source: string | object = {}): Result { + let parsedSource = typeof source === "string" ? JSON.parse(source) : source; + return new Result(parsedSource as Partial>); + } +} +``` + +### Interfaces + +The binding generator can generate TypeScript interfaces instead of classes using the `-i` flag: + +```bash +wails3 generate bindings -ts -i +``` + +This will generate TypeScript interfaces for all models: + +```typescript +export interface Person { + Name: string; + Attributes: Record; + Friends: string[]; +} +``` + +## Optimizing Binding Generation + +### Using Names Instead of IDs + +By default, the binding generator uses hash-based IDs for method calls. You can use the `-names` flag to use names instead: + +```bash +wails3 generate bindings -names +``` + +This will generate code that uses method names instead of IDs: + +```javascript +export function Greet(name) { + let $resultPromise = $Call.ByName("Greet", name); + return $resultPromise; +} +``` + +This can make the generated code more readable and easier to debug, but it may be slightly less efficient. + +### Bundling the Runtime + +By default, the generated code imports the Wails runtime from the `@wailsio/runtime` npm package. You can use the `-b` flag to bundle the runtime with the generated code: + +```bash +wails3 generate bindings -b +``` + +This will include the runtime code directly in the generated files, eliminating the need for the npm package. + +### Disabling Index Files + +If you don't need the index files, you can use the `-noindex` flag to disable their generation: + +```bash +wails3 generate bindings -noindex +``` + +This can be useful if you prefer to import services and models directly from their respective files. + +## Real-World Examples + +### Authentication Service + +Here's an example of an authentication service with custom directives: + +```go +package auth + +//wails:inject console.log("Auth service initialized"); +type AuthService struct { + // Private fields + users map[string]User +} + +type User struct { + Username string + Email string + Role string +} + +type LoginRequest struct { + Username string + Password string +} + +type LoginResponse struct { + Success bool + User User + Token string + Error string +} + +// Login authenticates a user +func (s *AuthService) Login(req LoginRequest) LoginResponse { + // Implementation... +} + +// GetCurrentUser returns the current user +func (s *AuthService) GetCurrentUser() User { + // Implementation... +} + +// Internal helper method +//wails:internal +func (s *AuthService) validateCredentials(username, password string) bool { + // Implementation... +} +``` + +### Data Processing Service + +Here's an example of a data processing service with generic types: + +```go +package data + +type ProcessingResult[T any] struct { + Data T + Error string +} + +type DataService struct {} + +// Process processes data and returns a result +func (s *DataService) Process(data string) ProcessingResult[map[string]int] { + // Implementation... +} + +// ProcessBatch processes multiple data items +func (s *DataService) ProcessBatch(data []string) ProcessingResult[[]map[string]int] { + // Implementation... +} + +// Internal helper method +//wails:internal +func (s *DataService) parseData(data string) (map[string]int, error) { + // Implementation... +} +``` + +### Conditional Code Injection + +Here's an example of conditional code injection for different output formats: + +```go +//wails:inject j*:/** +//wails:inject j*: * @param {string} arg +//wails:inject j*: * @returns {Promise} +//wails:inject j*: */ +//wails:inject j*:export async function CustomMethod(arg) { +//wails:inject t*:export async function CustomMethod(arg: string): Promise { +//wails:inject await InternalMethod("Hello " + arg + "!"); +//wails:inject } +type Service struct{} +``` + +This injects different code for JavaScript and TypeScript outputs, providing appropriate type annotations for each language. diff --git a/docs/src/content/docs/learn/application-menu.mdx b/docs/src/content/docs/learn/application-menu.mdx new file mode 100644 index 000000000..5613d09ff --- /dev/null +++ b/docs/src/content/docs/learn/application-menu.mdx @@ -0,0 +1,262 @@ +--- +title: Application Menu +sidebar: + order: 53 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Application menus provide the main menu bar interface for your application. They appear at the top of the window on Windows and Linux, and at the top of the screen on macOS. + +## Creating an Application Menu + +Create a new application menu using the `New` method from the Menu manager: + +```go +menu := app.Menu.New() +``` + +## Setting the Menu + +The way to set the menu varies on the platform: + + + + + On macOS, there is only one menu bar per application. Set the menu using the `Set` method of the Menu manager: + + ```go + app.Menu.Set(menu) + ``` + + + + + + On Windows, there is a menu bar per window. Set the menu using the `SetMenu` method of the window: + + ```go + app.Window.Current().SetMenu(menu) + ``` + + + + + + On Linux, the menu bar is typically per window. Set the menu using the `SetMenu` method of the window: + + ```go + app.Window.Current().SetMenu(menu) + ``` + + + + + +## Menu Roles + +Wails provides predefined menu roles that automatically create platform-appropriate menu structures: + +```go +// Add standard application menu on macOS +if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) +} + +// Add standard menus +menu.AddRole(application.FileMenu) +menu.AddRole(application.EditMenu) +menu.AddRole(application.WindowMenu) +menu.AddRole(application.HelpMenu) +``` + +:::note[Platform Behaviour] +The AppMenu role is specific to macOS and provides the standard application menu containing About, Preferences, and Quit items. +::: + +### Available Roles + +| Role | Description | Platform Notes | +|------|-------------|----------------| +| `AppMenu` | Standard application menu | macOS only | +| `FileMenu` | File operations menu | All platforms | +| `EditMenu` | Text editing operations | All platforms | +| `WindowMenu` | Window management | All platforms | +| `HelpMenu` | Help and information | All platforms | + +## Custom Menus + +Create custom menus by adding items directly: + +```go +// Add a custom menu +customMenu := menu.AddSubmenu("Tools") +customMenu.Add("Settings").OnClick(func(ctx *application.Context) { + // Show settings dialogue +}) +``` + +:::tip[Menu Items] +For detailed information about available menu item types and properties, refer to the [Menu Reference](./menu-reference) documentation. +::: + +## Window Control + +Menu items can control the application windows: + +```go +viewMenu := menu.AddSubmenu("View") +viewMenu.Add("Toggle Fullscreen").OnClick(func(ctx *application.Context) { + window := app.Window.Current() + if window.Fullscreen() { + window.SetFullscreen(false) + } else { + window.SetFullscreen(true) + } +}) +``` + +## Dynamic Menus + +Menus can be updated dynamically based on application state: + +```go +projectMenu := menu.AddSubmenu("Project") +saveItem := projectMenu.Add("Save Project") + +// Update based on state +saveItem.OnClick(func(ctx *application.Context) { + if projectSaved { + saveItem.SetEnabled(false) + saveItem.SetLabel("Project Saved") + } + menu.Update() +}) +``` + +## Platform-Specific Considerations + + + + + On macOS, menus are deeply integrated with the system: + + - Menus appear in the system menu bar at the top of the screen + - The application menu (⌘) is required and should be added using `menu.AddRole(application.AppMenu)` + - Standard keyboard shortcuts are automatically handled + - Menu styling follows system appearance + - The "About" menu item appears in the application menu + - Preferences are typically placed in the application menu + + + + + + On Windows, menus follow the traditional Windows UI guidelines: + + - Menus appear in the application window's title bar + - Standard keyboard shortcuts should be explicitly set using `SetAccelerator` + - Menu styling matches the Windows theme + - The "About" menu item typically appears in the Help menu + - Settings/Preferences are typically placed in the Tools menu + + + + + + On Linux, menu behaviour depends on the desktop environment: + + - Menu appearance adapts to the desktop environment's theme + - Some desktop environments (like Unity) support global menu bars + - Menu placement follows the desktop environment's conventions + - Keyboard shortcuts should be explicitly set + - Settings are typically placed in the Edit menu + + + + +## Best Practices + +1. Use standard menu roles where appropriate +2. Follow platform-specific menu conventions +3. Provide keyboard shortcuts for common actions +4. Keep menu structures shallow and organised +5. Update menu items to reflect application state +6. Use clear, concise menu labels +7. Group related items logically + +:::danger[Warning] +Always test menu functionality across all target platforms to ensure consistent behaviour and appearance. +::: + +:::tip[Pro Tip] +Consider using the `app.Window.Current()` method in menu handlers to affect the active window, rather than storing window references. +::: + +## Complete Example + +Here's a comprehensive example demonstrating various menu features: + +```go +package main + +import ( + "runtime" + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Menu Demo", + }) + + // Create main menu + menu := app.Menu.New() + + // Add platform-specific application menu + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + + // Add standard menus + fileMenu := menu.AddRole(application.FileMenu) + menu.AddRole(application.EditMenu) + menu.AddRole(application.WindowMenu) + menu.AddRole(application.HelpMenu) + + // Add custom menu + toolsMenu := menu.AddSubmenu("Tools") + + // Add checkbox item + toolsMenu.AddCheckbox("Dark Mode", false).OnClick(func(ctx *application.Context) { + isDark := ctx.ClickedMenuItem().Checked() + // Toggle theme + }) + + // Add radio group + toolsMenu.AddRadio("Small Text", true).OnClick(handleFontSize) + toolsMenu.AddRadio("Medium Text", false).OnClick(handleFontSize) + toolsMenu.AddRadio("Large Text", false).OnClick(handleFontSize) + + // Add submenu + advancedMenu := toolsMenu.AddSubmenu("Advanced") + advancedMenu.Add("Configure...").OnClick(func(ctx *application.Context) { + // Show configuration + }) + + // Set the menu + app.Menu.Set(menu) + + // Create main window + app.Window.New() + + err := app.Run() + if err != nil { + panic(err) + } +} + +func handleFontSize(ctx *application.Context) { + size := ctx.ClickedMenuItem().Label() + // Update font size +} diff --git a/docs/src/content/docs/learn/badges.mdx b/docs/src/content/docs/learn/badges.mdx new file mode 100644 index 000000000..3effaf7cc --- /dev/null +++ b/docs/src/content/docs/learn/badges.mdx @@ -0,0 +1,195 @@ +--- +title: Badges +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +## Introduction + +Wails provides a cross-platform badge service for desktop applications. This service allows you to display badges on your application tile or dock icon, which is useful for indicating unread messages, notifications, or other status information. + +## Basic Usage + +### Creating the Service + +First, initialize the badge service: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/pkg/services/badge" + +// Create a new badge service +badgeService := badge.New() + +// Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(badgeService), + }, +}) +``` + +### Creating the Service with Custom Options (Windows Only) + +On Windows, you can customize the badge appearance with various options: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/pkg/services/badge" +import "image/color" + +// Create a badge service with custom options +options := badge.Options{ + TextColour: color.RGBA{255, 255, 255, 255}, // White text + BackgroundColour: color.RGBA{0, 0, 255, 255}, // Blue background + FontName: "consolab.ttf", // Bold Consolas font + FontSize: 20, // Font size for single character + SmallFontSize: 14, // Font size for multiple characters +} + +badgeService := badge.NewWithOptions(options) + +// Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(badgeService), + }, +}) +``` + +## Badge Operations + +### Setting a Badge + +Set a badge on the application tile/dock icon: + +```go +// Set a default badge +badgeService.SetBadge("") + +// Set a numeric badge +badgeService.SetBadge("3") + +// Set a text badge +badgeService.SetBadge("New") +``` + +### Setting a Custom Badge + +Set a badge on the application tile/dock icon with one-off options applied: + +#### Go +```go +options := badge.Options{ + BackgroundColour: color.RGBA{0, 255, 255, 255}, + FontName: "arialb.ttf", // System font + FontSize: 16, + SmallFontSize: 10, + TextColour: color.RGBA{0, 0, 0, 255}, +} + +// Set a default badge +badgeService.SetCustomBadge("", options) + +// Set a numeric badge +badgeService.SetCustomBadge("3", options) + +// Set a text badge +badgeService.SetCustomBadge("New", options) +``` + +### Removing a Badge + +Remove the badge from the application icon: + +```go +badgeService.RemoveBadge() +``` + +## Platform Considerations + + + + + On macOS, badges: + + - Are displayed directly on the dock icon + - Support text values + - Automatically handle dark/light mode appearance + - Use the standard macOS dock badge styling + - Automatically handle label overflow + - Do not support customization options (any options passed to NewWithOptions will be ignored) + - Will display "●" as the default badge if an empty label is provided + + + + + + On Windows, badges: + + - Are displayed as an overlay icon in the taskbar + - Support text values + - Allow customization of colors, font, and font sizes + - Adapt to Windows theme settings + - Require the application to have a window + - Use smaller font size automatically for badges with multiple characters + - Do not handle label overflow + - Support the following customization options: + - TextColour: Text color (default: white) + - BackgroundColour: Badge background color (default: red) + - FontName: Font file name (default: "segoeuib.ttf") + - FontSize: Font size for single character (default: 18) + - SmallFontSize: Font size for multiple characters (default: 14) + + + + + + On Linux: + + - Badge functionality is not available + + + + +## Best Practices + +1. Use badges sparingly: + - Too many badge updates can distract users + - Reserve badges for important notifications + +2. Keep badge text short: + - Numeric badges are most effective + - On macOS, text badges should be brief + +3. For Windows customization: + - Ensure high contrast between text and background colors + - Test with different text lengths as font size decreases with length + - Use common system fonts to ensure availability + +## API Reference + +### Service Management +| Method | Description | +|--------------------------------------------|-------------------------------------------------------| +| `New()` | Creates a new badge service with default options | +| `NewWithOptions(options Options)` | Creates a new badge service with custom options (Windows only, options are ignored on macOS) | + +### Badge Operations +| Method | Description | +|----------------------------------------------|------------------------------------------------------------| +| `SetBadge(label string) error` | Sets a badge with the specified label | +| `SetCustomBadge(label string, options Options) error` | Sets a badge with the specified label and custom styling options (Windows only) | +| `RemoveBadge() error` | Removes the badge from the application icon | +### Structs and Types + +```go +// Options for customizing badge appearance (Windows only) +type Options struct { + TextColour color.RGBA // Color of the badge text + BackgroundColour color.RGBA // Color of the badge background + FontName string // Font file name (e.g., "segoeuib.ttf") + FontSize int // Font size for single character + SmallFontSize int // Font size for multiple characters +} +``` diff --git a/docs/src/content/docs/learn/binding-best-practices.mdx b/docs/src/content/docs/learn/binding-best-practices.mdx new file mode 100644 index 000000000..4536fc3c9 --- /dev/null +++ b/docs/src/content/docs/learn/binding-best-practices.mdx @@ -0,0 +1,115 @@ +--- +title: Binding Best Practices +sidebar: + order: 23 +--- + +import { FileTree } from "@astrojs/starlight/components"; + +This guide provides best practices and patterns for using the Wails binding system effectively in your applications. + +## Service Design Patterns + +### Service Organization + +Organize your services based on functionality rather than technical concerns. For example, instead of having a single large service, split it into smaller, focused services: + +```go +// Instead of this: +type AppService struct {} + +func (s *AppService) GetUser() User { /* ... */ } +func (s *AppService) UpdateUser(user User) error { /* ... */ } +func (s *AppService) GetProducts() []Product { /* ... */ } +func (s *AppService) AddProduct(product Product) error { /* ... */ } + +// Do this: +type UserService struct {} +func (s *UserService) GetUser() User { /* ... */ } +func (s *UserService) UpdateUser(user User) error { /* ... */ } + +type ProductService struct {} +func (s *ProductService) GetProducts() []Product { /* ... */ } +func (s *ProductService) AddProduct(product Product) error { /* ... */ } +``` + +This makes your code more maintainable and easier to understand. + +### Use JSON Tags + +Use JSON tags to control how your models are serialized: + +```go +type User struct { + ID int `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Password string `json:"-"` // Exclude from JSON + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} +``` + +### Separate Frontend and Backend Models + +Consider using different models for the frontend and backend: + +```go +// Backend model +type User struct { + ID int + Name string + Email string + Password string // Sensitive data + CreatedAt time.Time + UpdatedAt time.Time +} + +// Frontend model +type UserDTO struct { + ID int `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + CreatedAt time.Time `json:"created_at"` +} + +func (s *UserService) GetUser() UserDTO { + user := getUserFromDatabase() + return UserDTO{ + ID: user.ID, + Name: user.Name, + Email: user.Email, + CreatedAt: user.CreatedAt, + } +} +``` + +This gives you more control over what data is exposed to the frontend. + +### Use Context for Cancellation + +Use context for cancellation to avoid wasting resources on abandoned requests: + +```go +func (s *ProductService) GetProducts(ctx context.Context, req ProductsRequest) (ProductsResponse, error) { + // Check if the request has been cancelled + select { + case <-ctx.Done(): + return ProductsResponse{}, ctx.Err() + default: + // Continue processing + } + + products, total, err := getProductsFromDatabase(ctx, req.Page, req.PageSize, req.Filter) + if err != nil { + return ProductsResponse{}, err + } + + return ProductsResponse{ + Products: products, + Total: total, + Page: req.Page, + PageSize: req.PageSize, + }, nil +} +``` diff --git a/docs/src/content/docs/learn/binding-system.mdx b/docs/src/content/docs/learn/binding-system.mdx new file mode 100644 index 000000000..13ce15932 --- /dev/null +++ b/docs/src/content/docs/learn/binding-system.mdx @@ -0,0 +1,155 @@ +--- +title: Binding System Internals +sidebar: + order: 21 +--- + +import { FileTree } from "@astrojs/starlight/components"; + +This guide explains how the Wails binding system works internally, providing insights for developers who want to understand the mechanics behind the automatic code generation. + +## Architecture Overview + +The Wails binding system consists of three main components: + +1. **Collection**: Analyzes Go code to extract information about services, models, and other declarations +2. **Configuration**: Manages settings and options for the binding generation process +3. **Rendering**: Generates JavaScript/TypeScript code based on the collected information + + +- internal/generator/ + - collect/ # Package analysis and information extraction + - config/ # Configuration structures and interfaces + - render/ # Code generation for JS/TS + + +## Collection Process + +The collection process is responsible for analyzing Go packages and extracting information about services, models, and other declarations. This is handled by the `collect` package. + +### Key Components + +- **Collector**: Manages package information and caches collected data +- **Package**: Represents a Go package being analyzed and stores collected services, models, and directives +- **Service**: Collects information about service types and their methods +- **Model**: Collects detailed information about model types, including fields, values, and type parameters +- **Directive**: Parses and interprets `//wails:` directives in Go source code + +### Collection Flow + +1. The collector scans the Go packages specified in the project +2. It identifies service types (structs with methods that will be exposed to the frontend) +3. For each service, it collects information about its methods +4. It identifies model types (structs used as parameters or return values in service methods) +5. For each model, it collects information about its fields and type parameters +6. It processes any `//wails:` directives found in the code + +## Rendering Process + +The rendering process is responsible for generating JavaScript/TypeScript code based on the collected information. This is handled by the `render` package. + +### Key Components + +- **Renderer**: Orchestrates the rendering of service, model, and index files +- **Module**: Represents a single generated JavaScript/TypeScript module +- **Templates**: Text templates used for code generation + +### Rendering Flow + +1. For each service, the renderer generates a JavaScript/TypeScript file with functions that mirror the service methods +2. For each model, the renderer generates a JavaScript/TypeScript class that mirrors the model struct +3. The renderer generates index files that re-export all services and models +4. The renderer applies any custom code injections specified by `//wails:inject` directives + +## Type Mapping + +One of the most important aspects of the binding system is how Go types are mapped to JavaScript/TypeScript types. Here's a summary of the mapping: + +| Go Type | JavaScript Type | TypeScript Type | +|---------|----------------|----------------| +| `bool` | `boolean` | `boolean` | +| `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `float32`, `float64` | `number` | `number` | +| `string` | `string` | `string` | +| `[]byte` | `Uint8Array` | `Uint8Array` | +| `[]T` | `Array` | `T[]` | +| `map[K]V` | `Object` | `Record` | +| `struct` | `Object` | Custom class | +| `interface{}` | `any` | `any` | +| `*T` | `T \| null` | `T \| null` | +| `func` | Not supported | Not supported | +| `chan` | Not supported | Not supported | + +## Directives System + +The binding system supports several directives that can be used to customize the generated code. These directives are added as comments in your Go code. + +### Available Directives + +- `//wails:inject`: Injects custom JavaScript/TypeScript code into the generated bindings +- `//wails:include`: Includes additional files with the generated bindings +- `//wails:internal`: Marks a type or method as internal, preventing it from being exported to the frontend +- `//wails:ignore`: Completely ignores a method during binding generation +- `//wails:id`: Specifies a custom ID for a method, overriding the default hash-based ID + +### Directive Processing + +1. During the collection phase, the collector identifies and parses directives in the Go code +2. The directives are stored with the corresponding declarations (services, methods, models, etc.) +3. During the rendering phase, the renderer applies the directives to customize the generated code + +## Advanced Features + +### Conditional Code Generation + +The binding system supports conditional code generation using a two-character condition prefix for `include` and `inject` directives: + +``` + + + +
+

Application Report

+

Generated: {{.Timestamp}}

+
+
+ + {{range .Items}} +

{{.}}

+ {{end}} +
+ +` + + // Write report to file + file, err := os.Create(reportPath) + if err != nil { + return err + } + defer file.Close() + + t, err := template.New("report").Parse(tmpl) + if err != nil { + return err + } + + err = t.Execute(file, data) + if err != nil { + return err + } + + // Open in browser + return app.Browser.OpenFile(reportPath) +} +``` + +### Development Tools + +Open development resources during development: + +```go +func setupDevelopmentMenu(app *application.App) { + if !app.Env.Info().Debug { + return // Only show in debug mode + } + + menu := app.Menu.New() + devMenu := menu.AddSubmenu("Development") + + devMenu.Add("Open DevTools").OnClick(func(ctx *application.Context) { + // This would open browser devtools if available + window := app.Window.Current() + if window != nil { + window.OpenDevTools() + } + }) + + devMenu.Add("View Source").OnClick(func(ctx *application.Context) { + // Open source code repository + app.Browser.OpenURL("https://github.com/youruser/yourapp") + }) + + devMenu.Add("API Documentation").OnClick(func(ctx *application.Context) { + // Open local API docs + app.Browser.OpenURL("http://localhost:8080/docs") + }) +} +``` + +## Error Handling + +### Graceful Error Handling + +Always handle potential errors when opening URLs or files: + +```go +func openURLWithFallback(app *application.App, url string, fallbackMessage string) { + err := app.Browser.OpenURL(url) + if err != nil { + app.Logger.Error("Failed to open URL", "url", url, "error", err) + + // Show fallback dialog with URL + dialog := app.Dialog.Info() + dialog.SetTitle("Unable to Open Link") + dialog.SetMessage(fmt.Sprintf("%s\n\nURL: %s", fallbackMessage, url)) + dialog.Show() + } +} + +// Usage +openURLWithFallback(app, + "https://docs.example.com", + "Please open the following URL manually in your browser:") +``` + +### User Feedback + +Provide feedback when operations succeed or fail: + +```go +func openURLWithFeedback(app *application.App, url string) { + err := app.Browser.OpenURL(url) + if err != nil { + // Show error dialog + app.Dialog.Error(). + SetTitle("Browser Error"). + SetMessage(fmt.Sprintf("Could not open URL: %s", err.Error())). + Show() + } else { + // Optionally show success notification + app.Logger.Info("URL opened successfully", "url", url) + } +} +``` + +## Platform Considerations + + + + + On macOS: + + - Uses the `open` command to launch the default browser + - Respects user's default browser setting in System Preferences + - May prompt for permission if the application is sandboxed + - Handles `file://` URLs correctly for local files + + + + + + On Windows: + + - Uses Windows Shell API to open URLs + - Respects default browser setting in Windows Settings + - Handles Windows path formats correctly + - May show security warnings for untrusted URLs + + + + + + On Linux: + + - Attempts to use `xdg-open` first, falls back to other methods + - Behavior varies by desktop environment + - Respects `BROWSER` environment variable if set + - May require additional packages in minimal installations + + + + +## Best Practices + +1. **Always Handle Errors**: Browser operations can fail for various reasons: + ```go + if err := app.Browser.OpenURL(url); err != nil { + app.Logger.Error("Failed to open browser", "error", err) + // Provide fallback or user notification + } + ``` + +2. **Validate URLs**: Ensure URLs are well-formed before opening: + ```go + func isValidHTTPURL(str string) bool { + u, err := url.Parse(str) + return err == nil && (u.Scheme == "http" || u.Scheme == "https") + } + ``` + +3. **User Confirmation**: For external links, consider asking user permission: + ```go + // Show confirmation dialog before opening external links + confirmAndOpen(app, "https://external-site.com") + ``` + +4. **Secure File Paths**: When opening files, ensure paths are safe: + ```go + func openSafeFile(app *application.App, filename string) error { + // Ensure file exists and is readable + if _, err := os.Stat(filename); err != nil { + return err + } + return app.Browser.OpenFile(filename) + } + ``` + +## Complete Example + +Here's a complete example showing various browser integration patterns: + +```go +package main + +import ( + "fmt" + "os" + "path/filepath" + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Browser Integration Demo", + }) + + // Setup menu with browser actions + setupMenu(app) + + // Create main window + window := app.Window.New() + window.SetTitle("Browser Integration") + + err := app.Run() + if err != nil { + panic(err) + } +} + +func setupMenu(app *application.App) { + menu := app.Menu.New() + + // File menu + fileMenu := menu.AddSubmenu("File") + fileMenu.Add("Generate Report").OnClick(func(ctx *application.Context) { + generateHTMLReport(app) + }) + + // Help menu + helpMenu := menu.AddSubmenu("Help") + helpMenu.Add("Documentation").OnClick(func(ctx *application.Context) { + openWithConfirmation(app, "https://docs.example.com") + }) + helpMenu.Add("Support").OnClick(func(ctx *application.Context) { + openWithConfirmation(app, "https://support.example.com") + }) + + app.Menu.Set(menu) +} + +func openWithConfirmation(app *application.App, url string) { + dialog := app.Dialog.Question() + dialog.SetTitle("Open External Link") + dialog.SetMessage(fmt.Sprintf("Open %s in your browser?", url)) + + dialog.AddButton("Open").OnClick(func() { + if err := app.Browser.OpenURL(url); err != nil { + showError(app, "Failed to open URL", err) + } + }) + + dialog.AddButton("Cancel") + dialog.Show() +} + +func generateHTMLReport(app *application.App) { + // Create temporary HTML file + tmpDir := os.TempDir() + reportPath := filepath.Join(tmpDir, "demo_report.html") + + html := ` + + + + Demo Report + + + +
+

Application Report

+

This is a sample report generated by the application.

+
+
+

Report Details

+

This report was generated to demonstrate browser integration.

+
+ +` + + err := os.WriteFile(reportPath, []byte(html), 0644) + if err != nil { + showError(app, "Failed to create report", err) + return + } + + // Open in browser + err = app.Browser.OpenFile(reportPath) + if err != nil { + showError(app, "Failed to open report", err) + } +} + +func showError(app *application.App, message string, err error) { + app.Dialog.Error(). + SetTitle("Error"). + SetMessage(fmt.Sprintf("%s: %v", message, err)). + Show() +} +``` + +:::tip[Pro Tip] +Consider providing fallback mechanisms for when browser operations fail, such as copying URLs to clipboard or showing them in a dialog for manual opening. +::: + +:::danger[Warning] +Always validate URLs and file paths before opening them to prevent security issues. Be cautious about opening user-provided URLs without validation. +::: \ No newline at end of file diff --git a/docs/src/content/docs/learn/build.mdx b/docs/src/content/docs/learn/build.mdx new file mode 100644 index 000000000..c3ace7ea4 --- /dev/null +++ b/docs/src/content/docs/learn/build.mdx @@ -0,0 +1,281 @@ +--- +title: Build System +sidebar: + order: 40 +--- + +import { FileTree } from "@astrojs/starlight/components"; + +## Overview + +The Wails build system is a flexible and powerful tool designed to streamline +the build process for your Wails applications. It leverages +[Task](https://taskfile.dev), a task runner that allows you to define and run +tasks easily. While the v3 build system is the default, Wails encourages a +"bring your own tooling" approach, allowing developers to customize their build +process as needed. + +Learn more about how to use Task in the +[official documentation](https://taskfile.dev/usage/). + +## Task: The Heart of the Build System + +[Task](https://taskfile.dev) is a modern alternative to Make, written in Go. It +uses a YAML file to define tasks and their dependencies. In the Wails build +system, [Task](https://taskfile.dev) plays a central role in orchestrating the +build process. + +The main `Taskfile.yml` is located in the project root, while platform-specific +tasks are defined in `build//Taskfile.yml` files. A common `Taskfile.yml` +file in the `build` directory contains common tasks that are shared across +platforms. + + + +- Project Root + - Taskfile.yml + - build + - windows/Taskfile.yml + - darwin/Taskfile.yml + - linux/Taskfile.yml + - Taskfile.yml + + + +## Taskfile.yml + +The `Taskfile.yml` file in the project root is the main entry point for the build system. It defines +the tasks and their dependencies. Here's the default `Taskfile.yml` file: + +```yaml +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "myproject" + BIN_DIR: "bin" + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + + +``` + +## Platform-Specific Taskfiles + +Each platform has its own Taskfile, located in the platform directories beneath the `build` directory. These +files define the core tasks for that platform. Each taskfile includes common tasks from the `build/Taskfile.yml` file. + +### Windows + +Location: `build/windows/Taskfile.yml` + +The Windows-specific Taskfile includes tasks for building, packaging, and +running the application on Windows. Key features include: + +- Building with optional production flags +- Generating `.ico` icon file +- Generating Windows `.syso` file +- Creating an NSIS installer for packaging + +### Linux + +Location: `build/linux/Taskfile.yml` + +The Linux-specific Taskfile includes tasks for building, packaging, and running +the application on Linux. Key features include: + +- Building with optional production flags +- Creating an AppImage, deb, rpm, and Arch Linux packages +- Generating `.desktop` file for Linux applications + +### macOS + +Location: `build/darwin/Taskfile.yml` + +The macOS-specific Taskfile includes tasks for building, packaging, and running +the application on macOS. Key features include: + +- Building binaries for amd64, arm64 and universal (both) architectures +- Generating `.icns` icon file +- Creating an `.app` bundle for distributing +- Ad-hoc signing `.app` bundles +- Setting macOS-specific build flags and environment variables + +## Task Execution and Command Aliases + +The `wails3 task` command is an embedded version of [Taskfile](https://taskfile.dev), which executes +the tasks defined in your `Taskfile.yml`. + +The `wails3 build` and `wails3 package` commands are aliases for +`wails3 task build` and `wails3 task package` respectively. When you run these +commands, Wails internally translates them to the appropriate task execution: + +- `wails3 build` → `wails3 task build` +- `wails3 package` → `wails3 task package` + +### Passing Parameters to Tasks + +You can pass CLI variables to tasks using the `KEY=VALUE` format. These variables are forwarded through the alias commands: + +```bash +# These are equivalent: +wails3 build PLATFORM=linux CONFIG=production +wails3 task build PLATFORM=linux CONFIG=production + +# Package with custom version: +wails3 package VERSION=2.0.0 OUTPUT=myapp.pkg +``` + +In your `Taskfile.yml`, you can access these variables using Go template syntax: + +```yaml +tasks: + build: + cmds: + - echo "Building for {{.PLATFORM | default "darwin"}}" + - go build -tags {{.CONFIG | default "debug"}} -o myapp +``` + +## Common Build Process + +Across all platforms, the build process typically includes the following steps: + +1. Tidying Go modules +2. Building the frontend +3. Generating icons +4. Compiling the Go code with platform-specific flags +5. Packaging the application (platform-specific) + +## Customising the Build Process + +While the v3 build system provides a solid default configuration, you can easily +customise it to fit your project's needs. By modifying the `Taskfile.yml` and +platform-specific Taskfiles, you can: + +- Add new tasks +- Modify existing tasks +- Change the order of task execution +- Integrate with other tools and scripts + +This flexibility allows you to tailor the build process to your specific +requirements while still benefiting from the structure provided by the Wails +build system. + +:::tip[Learning Taskfile] +We highly recommend reading the [Taskfile](https://taskfile.dev) documentation to +understand how to use Taskfile effectively. +You can find out which version of Taskfile is embedded in the Wails CLI by running `wails3 task --version`. +::: + +## Development Mode + +The Wails build system includes a powerful development mode that enhances the +developer experience by providing live reloading and hot module replacement. +This mode is activated using the `wails3 dev` command. + +### How It Works + +When you run `wails3 dev`, the following process occurs: + +1. The command checks for an available port, defaulting to 9245 if not + specified. +2. It sets up the environment variables for the frontend dev server (Vite). +3. It starts the file watcher using the [refresh](https://github.com/atterpac/refresh) library. + +The [refresh](https://github.com/atterpac/refresh) library is responsible for +monitoring file changes and triggering rebuilds. It uses the configuration defined under the `dev_mode` key in the `./build/config.yaml` file. +It may be configured to ignore certain directories and files, to determine which files to watch and what actions to take when changes are detected. +The default configuration works pretty well, but feel free to customise it to your needs. + +### Configuration + +Here's an example of its structure: + +```yaml +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - cmd: wails3 task run + type: primary +``` + +This configuration file allows you to: + +- Set the root path for file watching +- Configure logging level +- Set a debounce time for file change events +- Ignore specific directories, files, or file extensions +- Define commands to execute on file changes + +### Customising Development Mode + +You can customise the development mode experience by modifying these values in the `config.yaml` file. + +Some ways to customise include: + +1. Changing the watched directories or files +2. Adjusting the debounce time to control how quickly the system responds to + changes +3. Adding or modifying the execute commands to fit your project's needs + +### Using a browser for development + +Whilst Wails v2 fully supported the use of a browser for development, it caused a lot +of confusion. Applications that would work in the browser would not necessarily +work in the desktop application, as not all browser APIs are available in webviews. + +For UI-focused development work, you still have the flexibility to use a browser +in v3, by accessing the Vite URL at `http://localhost:9245` in dev mode. This +gives you access to powerful browser dev tools while working on styling and +layout. Be aware that Go bindings *will not work* in this mode. +When you're ready to test functionality like bindings and events, simply +switch to the desktop view to ensure everything works perfectly in the +production environment. diff --git a/docs/src/content/docs/learn/clipboard.mdx b/docs/src/content/docs/learn/clipboard.mdx new file mode 100644 index 000000000..a31408dc7 --- /dev/null +++ b/docs/src/content/docs/learn/clipboard.mdx @@ -0,0 +1,135 @@ +--- +title: Clipboard +sidebar: + order: 50 +--- + +The Wails Clipboard API provides a simple interface for interacting with the system clipboard. It allows you to read from and write to the clipboard, whilst supporting text data. + +## Accessing the Clipboard + +The clipboard can be accessed through the application instance: + +```go +clipboard := app.Clipboard +``` + +## Setting Text + +To set text to the clipboard, utilise the `SetText` method: + +```go +success := app.Clipboard.SetText("Hello World") +if !success { + // Handle error +} +``` + +The `SetText` method returns a boolean indicating whether the operation was successful. + +:::tip[Empty Text] +Setting an empty string (`""`) effectively clears the text content from the clipboard. +::: + +## Getting Text + +To retrieve text from the clipboard, utilise the `Text` method: + +```go +text, ok := app.Clipboard.Text() +if !ok { + // Handle error +} else { + // Use the text +} +``` + +The `Text` method returns two values: +- The text content from the clipboard (string) +- A boolean indicating whether the operation was successful + +:::note[Platform Behaviour] +The clipboard behaviour might vary slightly amongst operating systems. Always check the return values to ensure operations were successful. +::: + +## Example + +Here's a complete example showing how to create a menu-driven application that demonstrates clipboard operations: + +```go +package main + +import ( + "log" + "runtime" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Clipboard Demo", + Description: "A demo of the clipboard API", + Assets: application.AlphaAssets, + }) + + // Create a custom menu + menu := app.Menu.New() + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + + // Add clipboard operations to menu + setClipboardMenu := menu.AddSubmenu("Set Clipboard") + setClipboardMenu.Add("Set Text 'Hello'").OnClick(func(ctx *application.Context) { + success := app.Clipboard.SetText("Hello") + if !success { + app.Dialog.Info().SetMessage("Failed to set clipboard text").Show() + } + }) + + getClipboardMenu := menu.AddSubmenu("Get Clipboard") + getClipboardMenu.Add("Get Text").OnClick(func(ctx *application.Context) { + result, ok := app.Clipboard.Text() + if !ok { + app.Dialog.Info().SetMessage("Failed to get clipboard text").Show() + } else { + app.Dialog.Info().SetMessage("Got:\n\n" + result).Show() + } + }) + + clearClipboardMenu := menu.AddSubmenu("Clear Clipboard") + clearClipboardMenu.Add("Clear Text").OnClick(func(ctx *application.Context) { + success := app.Clipboard.SetText("") + if success { + app.Dialog.Info().SetMessage("Clipboard text cleared").Show() + } else { + app.Dialog.Info().SetMessage("Clipboard text not cleared").Show() + } + }) + + app.Menu.Set(menu) + app.Window.New() + + err := app.Run() + if err != nil { + log.Fatal(err.Error()) + } +} +``` + +:::danger[Warning] +Always handle clipboard operation failures gracefully, as they can fail due to various system-level reasons such as permissions or resource constraints. +::: + +## Best Practices + +1. Always check the return values of clipboard operations +2. Handle failures gracefully with appropriate user feedback +3. Clear sensitive data from the clipboard when your application exits if it was responsible for putting it there +4. Consider implementing a timeout mechanism for clipboard operations in critical sections of your application + +:::tip[Pro Tip] +Whilst working with the clipboard in a production environment, consider implementing retry logic for critical clipboard operations, as they can occasionally fail due to temporary system conditions. +::: diff --git a/docs/src/content/docs/learn/context-menu.mdx b/docs/src/content/docs/learn/context-menu.mdx new file mode 100644 index 000000000..8d70822cb --- /dev/null +++ b/docs/src/content/docs/learn/context-menu.mdx @@ -0,0 +1,237 @@ +--- +title: Context Menus +sidebar: + order: 51 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Context menus are popup menus that appear when right-clicking elements in your application. They provide quick access to relevant actions for the clicked element. + +## Creating a Context Menu + +To create a context menu, use the `Add` method from the ContextMenu manager: + +```go +contextMenu := app.ContextMenu.Add("menu-id", application.NewContextMenu()) +``` + +The `menu-id` parameter is a unique identifier for the menu that will be used to associate it with HTML elements. + +## Adding Menu Items + +You can add items to your context menu using the same methods as application menus. Here's a simple example: + +```go +contextMenu := application.NewContextMenu() +contextMenu.Add("Cut").OnClick(func(ctx *application.Context) { + // Handle cut action +}) +contextMenu.Add("Copy").OnClick(func(ctx *application.Context) { + // Handle copy action +}) +contextMenu.Add("Paste").OnClick(func(ctx *application.Context) { + // Handle paste action +}) + +// Register the context menu with the manager +app.ContextMenu.Add("editor-menu", contextMenu) +``` + +:::tip[Menu Items] +For detailed information about available menu item types and properties, refer to the [Menu Reference](./menu-reference) documentation. +::: + +## Context Data + +Context menus can receive data from the HTML element that triggered them. This data can be accessed in the click handlers: + +```go +contextMenu := app.ContextMenu.New() +menuItem := contextMenu.Add("Process Image") +menuItem.OnClick(func(ctx *application.Context) { + imageID := ctx.ContextMenuData() + // Process the image using the ID +}) + +// Register the context menu with the manager +app.ContextMenu.Add("image-menu", contextMenu) +``` + +## Associating with HTML Elements + +To associate a context menu with an HTML element, use the `--custom-contextmenu` and `--custom-contextmenu-data` CSS properties: + +```html +
+ Right click me! +
+``` + +- `--custom-contextmenu`: Specifies the menu ID (must match the ID used in `NewContextMenu`) +- `--custom-contextmenu-data`: Optional data that will be passed to the click handlers + +:::note +This feature will only work as expected after the runtime [has been initialised](../runtime#initialisation). +::: + +## Default Context Menu + +The default context menu is the webview's built-in context menu that provides system-level operations. You can control its visibility using the `--default-contextmenu` CSS property: + +```html + +
+ No default menu here +
+ + +
+ Default menu always shown +
+ + +
+ Shows menu when appropriate +
+``` + +:::note[Smart Context Menu] +The `auto` setting enables "smart" context menu behaviour: +- Shows when text is selected +- Shows in text input fields +- Shows in editable content +- Hides in other contexts +::: + +## Updating Menu Items + +Menu items can be updated dynamically using the `SetLabel` method and other property setters. After making changes, call `Update` on the menu to apply them: + +```go +contextMenu := application.NewContextMenu() +menuItem := contextMenu.Add("Initial Label") + +// Register the context menu with the manager +app.ContextMenu.Add("dynamic-menu", contextMenu) + +// Later, update the menu item +menuItem.SetLabel("New Label") +contextMenu.Update() +``` + +## Platform Considerations + + + + + On macOS, context menus follow system conventions: + + - Menus use native system animations and transitions + - Right-click is automatically mapped to Control+Click + - Menu styling automatically adapts to system appearance + - Standard text operations appear in the default context menu + - Context menus support native macOS scrolling behaviour + + + + + + On Windows, context menus integrate with the Windows UI: + + - Menus use the Windows native context menu style + - Right-click handling is native + - Menu appearance follows the Windows theme + - Default context menu includes standard Windows operations + - Context menus support Windows touch and pen input + + + + + + On Linux, context menu behaviour varies by desktop environment: + + - Menu styling adapts to the current desktop theme + - Right-click behaviour follows system settings + - Default context menu content may vary by environment + - Menu positioning follows desktop environment conventions + - GTK/Qt integration depends on the environment + + + + +:::tip[Pro Tip] +Consider using different context menus for different types of elements in your application. This allows you to provide context-specific actions that make sense for each element type. +::: + +## Best Practices + +1. Keep context menus focused and relevant to the clicked element +2. Use clear, concise labels for menu items +3. Group related items together +4. Consider using separators to organise menu items +5. Provide keyboard shortcuts for common actions +6. Update menu items dynamically based on application state +7. Handle errors gracefully when processing context data + +:::danger[Warning] +Always validate context data received from the frontend before using it in your application logic, as it could be manipulated by users. +::: + +## Example + +Here's a complete example demonstrating context menu features: + +```go +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Context Menu Demo", + }) + + // Create a context menu + contextMenu := app.ContextMenu.New() + + // Add items that respond to context data + clickMe := contextMenu.Add("Click to show context data") + dataLabel := contextMenu.Add("Current data: None") + + clickMe.OnClick(func(ctx *application.Context) { + data := ctx.ContextMenuData() + dataLabel.SetLabel("Current data: " + data) + contextMenu.Update() + }) + + // Register the context menu with the manager + app.ContextMenu.Add("test", contextMenu) + + window := app.Window.New() + window.SetTitle("Context Menu Demo") + + err := app.Run() + if err != nil { + panic(err) + } +} +``` + +Associated HTML: + +```html +
+ Right click me to see the custom menu! +
+ +
+ No context menu here +
+ +
+

Select this text to see the default menu

+ +
diff --git a/docs/src/content/docs/learn/dialogs.mdx b/docs/src/content/docs/learn/dialogs.mdx new file mode 100644 index 000000000..783f7bf8b --- /dev/null +++ b/docs/src/content/docs/learn/dialogs.mdx @@ -0,0 +1,232 @@ +--- +title: Dialogs +sidebar: + order: 54 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Wails provides a comprehensive dialog system for displaying native system dialogs. These include informational messages, questions, file selection, and more. + +## Dialog Types + +### Information Dialog + +Display simple informational messages to users: + +```go +dialog := app.Dialog.Info() +dialog.SetTitle("Welcome") +dialog.SetMessage("Welcome to our application!") +dialog.Show() +``` + +### Question Dialog + +Present users with questions and customisable buttons: + +```go +dialog := app.Dialog.Question() +dialog.SetTitle("Save Changes") +dialog.SetMessage("Do you want to save your changes?") +dialog.AddButton("Save").OnClick(func() { + // Handle save +}) +saveButton := dialog.AddButton("Don't Save") +dialog.SetDefaultButton(saveButton) +dialog.Show() +``` + +### Error Dialog + +Display error messages: + +```go +dialog := app.Dialog.Error() +dialog.SetTitle("Error") +dialog.SetMessage("Failed to save file") +dialog.Show() +``` + +### File Dialogs + +#### Open File Dialog + +Allow users to select files to open: + +```go +dialog := app.Dialog.OpenFile() +dialog.SetTitle("Select Image") +dialog.SetFilters([]*application.FileFilter{ + { + DisplayName: "Images (*.png;*.jpg)", + Pattern: "*.png;*.jpg", + }, +}) + +// Single file selection +if path, err := dialog.PromptForSingleSelection(); err == nil { + // Use selected file path +} + +// Multiple file selection +if paths, err := dialog.PromptForMultipleSelection(); err == nil { + // Use selected file paths +} +``` + +#### Save File Dialog + +Allow users to choose where to save files: + +```go +dialog := app.Dialog.SaveFile() +dialog.SetTitle("Save Document") +dialog.SetDefaultFilename("document.txt") +dialog.SetFilters([]*application.FileFilter{ + { + DisplayName: "Text Files (*.txt)", + Pattern: "*.txt", + }, +}) + +if path, err := dialog.PromptForSingleSelection(); err == nil { + // Save file to selected path +} +``` + +## Dialog Customisation + +### Setting Icons + +Dialogs can use custom icons from the built-in icon set: + +```go +dialog := app.Dialog.Info() +dialog.SetIcon(icons.ApplicationDarkMode256) +``` + +### Window Attachment + +Dialogs can be attached to specific windows: + +```go +dialog := app.Dialog.Question() +dialog.AttachToWindow(app.Window.Current()) +dialog.Show() +``` + +### Button Customisation + +Create buttons with custom labels and actions: + +```go +dialog := app.Dialog.Question() +dialog.SetMessage("Choose an action") + +// Add buttons with custom handlers +dialog.AddButton("Save").OnClick(func() { + // Handle save +}) +dialog.AddButton("Don't Save").OnClick(func() { + // Handle don't save +}) +cancelButton := dialog.AddButton("Cancel") +dialog.SetDefaultButton(cancelButton) // Set default button +``` + +## Platform Considerations + + + + + On macOS, dialogs follow system conventions: + + - Use system-standard dialog appearances + - Support keyboard navigation (Tab, Space, Return) + - Support standard keyboard shortcuts (⌘+.) + - Automatically handle dark/light mode + - Support system accessibility features + - Position relative to parent window + + + + + + On Windows, dialogs integrate with the Windows UI: + + - Use Windows system dialog styles + - Support keyboard navigation (Tab, Space, Enter) + - Support Windows accessibility features + - Follow Windows dialog positioning rules + - Adapt to Windows theme settings + - Support high DPI displays + + + + + + On Linux, dialog behaviour depends on the desktop environment: + + - Use native dialog widgets when available + - Follow desktop environment theme + - Support keyboard navigation + - Adapt to desktop environment settings + - Position according to window manager rules + - Support desktop environment accessibility + + + + +## Directory Selection + +Allow users to select directories: + +```go +dialog := app.Dialog.OpenFile() +dialog.CanChooseDirectories(true) +dialog.CanChooseFiles(false) +dialog.SetTitle("Select Project Directory") +if path, err := dialog.PromptForSingleSelection(); err == nil { + // Use selected directory path +} +``` + +## About Dialog + +Display application information: + +```go +app.Menu.ShowAbout() +``` + +## Best Practices + +1. Use appropriate dialog types for different scenarios: + - InfoDialog for general messages + - QuestionDialog for user decisions + - ErrorDialog for error messages + - FileDialog for file operations + +2. Provide clear and concise messages: + - Use descriptive titles + - Keep messages brief but informative + - Clearly state any required user action + +3. Handle dialog responses appropriately: + - Check for errors in file dialogs + - Provide feedback for user actions + - Handle cancellation gracefully + +4. Consider platform conventions: + - Follow platform-specific dialog patterns + - Use appropriate button ordering + - Respect system settings + +:::tip[Pro Tip] +When using file dialogs, always set appropriate filters to help users select the correct file types for your application. +::: + +:::danger[Warning] +Always handle potential errors from file and directory dialogs, as they may fail due to permissions or other system issues. +::: diff --git a/docs/src/content/docs/learn/environment.mdx b/docs/src/content/docs/learn/environment.mdx new file mode 100644 index 000000000..d03fb5f70 --- /dev/null +++ b/docs/src/content/docs/learn/environment.mdx @@ -0,0 +1,620 @@ +--- +title: Environment +sidebar: + order: 59 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Wails provides comprehensive environment information through the EnvironmentManager API. This allows your application to detect system properties, theme preferences, and integrate with the operating system's file manager. + +## Accessing the Environment Manager + +The environment manager is accessed through the `Env` property on your application instance: + +```go +app := application.New(application.Options{ + Name: "Environment Demo", +}) + +// Access the environment manager +env := app.Env +``` + +## System Information + +### Get Environment Information + +Retrieve comprehensive information about the runtime environment: + +```go +envInfo := app.Env.Info() + +app.Logger.Info("Environment information", + "os", envInfo.OS, // "windows", "darwin", "linux" + "arch", envInfo.Arch, // "amd64", "arm64", etc. + "debug", envInfo.Debug, // Debug mode flag +) + +// Operating system details +if envInfo.OSInfo != nil { + app.Logger.Info("OS details", + "name", envInfo.OSInfo.Name, + "version", envInfo.OSInfo.Version, + ) +} + +// Platform-specific information +for key, value := range envInfo.PlatformInfo { + app.Logger.Info("Platform info", "key", key, "value", value) +} +``` + +### Environment Structure + +The environment information includes several important fields: + +```go +type EnvironmentInfo struct { + OS string // Operating system: "windows", "darwin", "linux" + Arch string // Architecture: "amd64", "arm64", "386", etc. + Debug bool // Whether running in debug mode + OSInfo *operatingsystem.OS // Detailed OS information + PlatformInfo map[string]any // Platform-specific details +} +``` + +## Theme Detection + +### Dark Mode Detection + +Detect whether the system is using dark mode: + +```go +if app.Env.IsDarkMode() { + app.Logger.Info("System is in dark mode") + // Apply dark theme to your application + applyDarkTheme() +} else { + app.Logger.Info("System is in light mode") + // Apply light theme to your application + applyLightTheme() +} +``` + +### Theme Change Monitoring + +Listen for theme changes to update your application dynamically: + +```go +import "github.com/wailsapp/wails/v3/pkg/events" + +// Listen for theme changes +app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) { + if app.Env.IsDarkMode() { + app.Logger.Info("Switched to dark mode") + updateApplicationTheme("dark") + } else { + app.Logger.Info("Switched to light mode") + updateApplicationTheme("light") + } +}) + +func updateApplicationTheme(theme string) { + // Update your application's theme + // This could emit an event to the frontend + app.Event.Emit("theme:changed", theme) +} +``` + +## File Manager Integration + +### Open File Manager + +Open the system's file manager at a specific location: + +```go +// Open file manager at a directory +err := app.Env.OpenFileManager("/Users/username/Documents", false) +if err != nil { + app.Logger.Error("Failed to open file manager", "error", err) +} + +// Open file manager and select a specific file +err = app.Env.OpenFileManager("/Users/username/Documents/report.pdf", true) +if err != nil { + app.Logger.Error("Failed to open file manager with selection", "error", err) +} +``` + +### Common Use Cases + +Show files or folders in the file manager from your application: + +```go +func showInFileManager(app *application.App, path string) { + err := app.Env.OpenFileManager(path, true) + if err != nil { + // Fallback: try opening just the directory + dir := filepath.Dir(path) + err = app.Env.OpenFileManager(dir, false) + if err != nil { + app.Logger.Error("Could not open file manager", "path", path, "error", err) + + // Show error to user + app.Dialog.Error(). + SetTitle("File Manager Error"). + SetMessage("Could not open file manager"). + Show() + } + } +} + +// Usage examples +func setupFileMenu(app *application.App) { + menu := app.Menu.New() + fileMenu := menu.AddSubmenu("File") + + fileMenu.Add("Show Downloads Folder").OnClick(func(ctx *application.Context) { + homeDir, _ := os.UserHomeDir() + downloadsDir := filepath.Join(homeDir, "Downloads") + showInFileManager(app, downloadsDir) + }) + + fileMenu.Add("Show Application Data").OnClick(func(ctx *application.Context) { + configDir, _ := os.UserConfigDir() + appDir := filepath.Join(configDir, "MyApp") + showInFileManager(app, appDir) + }) +} +``` + +## Platform-Specific Behavior + +### Adaptive Application Behavior + +Use environment information to adapt your application's behavior: + +```go +func configureForPlatform(app *application.App) { + envInfo := app.Env.Info() + + switch envInfo.OS { + case "darwin": + configureMacOS(app) + case "windows": + configureWindows(app) + case "linux": + configureLinux(app) + } + + // Adapt to architecture + if envInfo.Arch == "arm64" { + app.Logger.Info("Running on ARM architecture") + // Potentially optimize for ARM + } +} + +func configureMacOS(app *application.App) { + app.Logger.Info("Configuring for macOS") + + // macOS-specific configuration + menu := app.Menu.New() + menu.AddRole(application.AppMenu) // Add standard macOS app menu + + // Handle dark mode + if app.Env.IsDarkMode() { + setMacOSDarkTheme() + } +} + +func configureWindows(app *application.App) { + app.Logger.Info("Configuring for Windows") + + // Windows-specific configuration + // Set up Windows-style menus, key bindings, etc. +} + +func configureLinux(app *application.App) { + app.Logger.Info("Configuring for Linux") + + // Linux-specific configuration + // Adapt to different desktop environments +} +``` + +## Debug Mode Handling + +### Development vs Production + +Use debug mode information to enable development features: + +```go +func setupApplicationMode(app *application.App) { + envInfo := app.Env.Info() + + if envInfo.Debug { + app.Logger.Info("Running in debug mode") + setupDevelopmentFeatures(app) + } else { + app.Logger.Info("Running in production mode") + setupProductionFeatures(app) + } +} + +func setupDevelopmentFeatures(app *application.App) { + // Enable development-only features + menu := app.Menu.New() + + // Add development menu + devMenu := menu.AddSubmenu("Development") + devMenu.Add("Reload Application").OnClick(func(ctx *application.Context) { + // Reload the application + window := app.Window.Current() + if window != nil { + window.Reload() + } + }) + + devMenu.Add("Open DevTools").OnClick(func(ctx *application.Context) { + window := app.Window.Current() + if window != nil { + window.OpenDevTools() + } + }) + + devMenu.Add("Show Environment").OnClick(func(ctx *application.Context) { + showEnvironmentDialog(app) + }) +} + +func setupProductionFeatures(app *application.App) { + // Production-only features + // Disable debug logging, enable analytics, etc. +} +``` + +## Environment Information Dialog + +### Display System Information + +Create a dialog showing environment information: + +```go +func showEnvironmentDialog(app *application.App) { + envInfo := app.Env.Info() + + details := fmt.Sprintf(`Environment Information: + +Operating System: %s +Architecture: %s +Debug Mode: %t + +Dark Mode: %t + +Platform Information:`, + envInfo.OS, + envInfo.Arch, + envInfo.Debug, + app.Env.IsDarkMode()) + + // Add platform-specific details + for key, value := range envInfo.PlatformInfo { + details += fmt.Sprintf("\n%s: %v", key, value) + } + + if envInfo.OSInfo != nil { + details += fmt.Sprintf("\n\nOS Details:\nName: %s\nVersion: %s", + envInfo.OSInfo.Name, + envInfo.OSInfo.Version) + } + + dialog := app.Dialog.Info() + dialog.SetTitle("Environment Information") + dialog.SetMessage(details) + dialog.Show() +} +``` + +## Platform Considerations + + + + + On macOS: + + - Dark mode detection uses system appearance settings + - File manager operations use Finder + - Platform info includes macOS version details + - Architecture may be "arm64" on Apple Silicon Macs + + ```go + if envInfo.OS == "darwin" { + // macOS-specific handling + if envInfo.Arch == "arm64" { + app.Logger.Info("Running on Apple Silicon") + } + } + ``` + + + + + + On Windows: + + - Dark mode detection uses Windows theme settings + - File manager operations use Windows Explorer + - Platform info includes Windows version details + - May include additional Windows-specific information + + ```go + if envInfo.OS == "windows" { + // Windows-specific handling + for key, value := range envInfo.PlatformInfo { + if key == "windows_version" { + app.Logger.Info("Windows version", "version", value) + } + } + } + ``` + + + + + + On Linux: + + - Dark mode detection varies by desktop environment + - File manager operations use system default file manager + - Platform info includes distribution details + - Behavior may vary between different Linux distributions + + ```go + if envInfo.OS == "linux" { + // Linux-specific handling + if distro, ok := envInfo.PlatformInfo["distribution"]; ok { + app.Logger.Info("Linux distribution", "distro", distro) + } + } + ``` + + + + +## Best Practices + +1. **Cache Environment Information**: Environment info rarely changes during runtime: + ```go + type App struct { + envInfo *application.EnvironmentInfo + } + + func (a *App) getEnvInfo() application.EnvironmentInfo { + if a.envInfo == nil { + info := a.app.Env.Info() + a.envInfo = &info + } + return *a.envInfo + } + ``` + +2. **Handle Theme Changes**: Listen for system theme changes: + ```go + app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) { + updateTheme(app.Env.IsDarkMode()) + }) + ``` + +3. **Graceful File Manager Failures**: Always handle file manager errors: + ```go + func openFileManagerSafely(app *application.App, path string) { + err := app.Env.OpenFileManager(path, false) + if err != nil { + // Provide fallback or user notification + app.Logger.Warn("Could not open file manager", "path", path) + } + } + ``` + +4. **Platform-Specific Features**: Use environment info to enable platform features: + ```go + envInfo := app.Env.Info() + if envInfo.OS == "darwin" { + // Enable macOS-specific features + } + ``` + +## Complete Example + +Here's a complete example demonstrating environment management: + +```go +package main + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func main() { + app := application.New(application.Options{ + Name: "Environment Demo", + }) + + // Setup application based on environment + setupForEnvironment(app) + + // Monitor theme changes + monitorThemeChanges(app) + + // Create menu with environment features + setupEnvironmentMenu(app) + + // Create main window + window := app.Window.New() + window.SetTitle("Environment Demo") + + err := app.Run() + if err != nil { + panic(err) + } +} + +func setupForEnvironment(app *application.App) { + envInfo := app.Env.Info() + + app.Logger.Info("Application environment", + "os", envInfo.OS, + "arch", envInfo.Arch, + "debug", envInfo.Debug, + "darkMode", app.Env.IsDarkMode(), + ) + + // Configure for platform + switch envInfo.OS { + case "darwin": + app.Logger.Info("Configuring for macOS") + // macOS-specific setup + case "windows": + app.Logger.Info("Configuring for Windows") + // Windows-specific setup + case "linux": + app.Logger.Info("Configuring for Linux") + // Linux-specific setup + } + + // Apply initial theme + if app.Env.IsDarkMode() { + applyDarkTheme(app) + } else { + applyLightTheme(app) + } +} + +func monitorThemeChanges(app *application.App) { + app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) { + if app.Env.IsDarkMode() { + app.Logger.Info("System switched to dark mode") + applyDarkTheme(app) + } else { + app.Logger.Info("System switched to light mode") + applyLightTheme(app) + } + }) +} + +func setupEnvironmentMenu(app *application.App) { + menu := app.Menu.New() + + // Add platform-specific app menu + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + + // Tools menu + toolsMenu := menu.AddSubmenu("Tools") + + toolsMenu.Add("Show Environment Info").OnClick(func(ctx *application.Context) { + showEnvironmentInfo(app) + }) + + toolsMenu.Add("Open Downloads Folder").OnClick(func(ctx *application.Context) { + openDownloadsFolder(app) + }) + + toolsMenu.Add("Toggle Theme").OnClick(func(ctx *application.Context) { + // This would typically be handled by the system + // but shown here for demonstration + toggleTheme(app) + }) + + app.Menu.Set(menu) +} + +func showEnvironmentInfo(app *application.App) { + envInfo := app.Env.Info() + + message := fmt.Sprintf(`Environment Information: + +Operating System: %s +Architecture: %s +Debug Mode: %t +Dark Mode: %t + +Platform Details:`, + envInfo.OS, + envInfo.Arch, + envInfo.Debug, + app.Env.IsDarkMode()) + + for key, value := range envInfo.PlatformInfo { + message += fmt.Sprintf("\n%s: %v", key, value) + } + + if envInfo.OSInfo != nil { + message += fmt.Sprintf("\n\nOS Information:\nName: %s\nVersion: %s", + envInfo.OSInfo.Name, + envInfo.OSInfo.Version) + } + + dialog := app.Dialog.Info() + dialog.SetTitle("Environment Information") + dialog.SetMessage(message) + dialog.Show() +} + +func openDownloadsFolder(app *application.App) { + homeDir, err := os.UserHomeDir() + if err != nil { + app.Logger.Error("Could not get home directory", "error", err) + return + } + + downloadsDir := filepath.Join(homeDir, "Downloads") + err = app.Env.OpenFileManager(downloadsDir, false) + if err != nil { + app.Logger.Error("Could not open Downloads folder", "error", err) + + app.Dialog.Error(). + SetTitle("File Manager Error"). + SetMessage("Could not open Downloads folder"). + Show() + } +} + +func applyDarkTheme(app *application.App) { + app.Logger.Info("Applying dark theme") + // Emit theme change to frontend + app.Event.Emit("theme:apply", "dark") +} + +func applyLightTheme(app *application.App) { + app.Logger.Info("Applying light theme") + // Emit theme change to frontend + app.Event.Emit("theme:apply", "light") +} + +func toggleTheme(app *application.App) { + // This is just for demonstration + // Real theme changes should come from the system + currentlyDark := app.Env.IsDarkMode() + if currentlyDark { + applyLightTheme(app) + } else { + applyDarkTheme(app) + } +} +``` + +:::tip[Pro Tip] +Use environment information to provide platform-appropriate user experiences. For example, use Command key shortcuts on macOS and Control key shortcuts on Windows/Linux. +::: + +:::danger[Warning] +Environment information is generally stable during application runtime, but theme preferences can change. Always listen for theme change events to keep your UI synchronized. +::: \ No newline at end of file diff --git a/docs/src/content/docs/learn/events.mdx b/docs/src/content/docs/learn/events.mdx new file mode 100644 index 000000000..fbdaabef9 --- /dev/null +++ b/docs/src/content/docs/learn/events.mdx @@ -0,0 +1,676 @@ +--- +title: Events +sidebar: + order: 55 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Wails provides a flexible event system that enables communication between different parts of your application. This includes both application-level and window-level events. + +## Application Events + +Application events are triggered by application-level state changes such as application startup, theme changes, and power events. You can listen for these events using the `OnApplicationEvent` method: + +```go +app.Event.OnApplicationEvent(events.Mac.ApplicationDidBecomeActive, func(event *application.ApplicationEvent) { + app.Logger.Info("Application started!") +}) + +app.Event.OnApplicationEvent(events.Windows.SystemThemeChanged, func(event *application.ApplicationEvent) { + app.Logger.Info("System theme changed!") + if event.Context().IsDarkMode() { + app.Logger.Info("System is now using dark mode!") + } else { + app.Logger.Info("System is now using light mode!") + } + }) +``` + +### Common Application Events + +Common application events are aliases for platform-specific application events. These events are triggered by application-level state +changes such as application startup, theme changes, and power events. + +Here is the same example as above, but using common application events to make it work across all platforms: + +```go +app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) { + app.Logger.Info("Application started!") +}) + +app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) { + if event.Context().IsDarkMode() { + app.Logger.Info("System is now using dark mode!") + } else { + app.Logger.Info("System is now using light mode!") + } +}) +``` +#### Common Application Event List + +| Event Name | Description | +|---------------------------|----------------------------------------------------------------------------------------------------------| +| ApplicationOpenedWithFile | Application opened with a file. See [File Associations](/guides/file-associations) for more information. | +| ApplicationStarted | Application has started | +| ThemeChanged | System theme changed | + +### Platform-Specific Application Events + +Below is a list of all platform-specific application events. + + + + + | Event Name | Common Event | Description | + |------------|--------------|-------------| + | ApplicationDidBecomeActive | - | Application became active | + | ApplicationDidChangeBackingProperties | - | Application backing properties changed | + | ApplicationDidChangeEffectiveAppearance | ThemeChanged | Application appearance changed | + | ApplicationDidChangeIcon | - | Application icon changed | + | ApplicationDidChangeOcclusionState | - | Application occlusion state changed | + | ApplicationDidChangeScreenParameters | - | Screen parameters changed | + | ApplicationDidChangeStatusBarFrame | - | Status bar frame changed | + | ApplicationDidChangeStatusBarOrientation | - | Status bar orientation changed | + | ApplicationDidChangeTheme | ThemeChanged | System theme changed | + | ApplicationDidFinishLaunching | ApplicationStarted | Application finished launching | + | ApplicationDidHide | - | Application hidden | + | ApplicationDidResignActiveNotification | - | Application resigned active state | + | ApplicationDidUnhide | - | Application unhidden | + | ApplicationDidUpdate | - | Application updated | + | ApplicationShouldHandleReopen | - | Application should handle reopen | + | ApplicationWillBecomeActive | - | Application will become active | + | ApplicationWillFinishLaunching | - | Application will finish launching | + | ApplicationWillHide | - | Application will hide | + | ApplicationWillResignActiveNotification | - | Application will resign active state | + | ApplicationWillTerminate | - | Application will terminate | + | ApplicationWillUnhide | - | Application will unhide | + | ApplicationWillUpdate | - | Application will update | + + + + + + | Event Name | Common Event | Description | + |------------|--------------|-------------| + | APMPowerSettingChange | - | Power settings changed | + | APMPowerStatusChange | - | Power status changed | + | APMResumeAutomatic | - | System resuming automatically | + | APMResumeSuspend | - | System resuming from suspend | + | APMSuspend | - | System suspending | + | ApplicationStarted | ApplicationStarted | Application started | + | SystemThemeChanged | ThemeChanged | System theme changed | + + + + | Event Name | Common Event | Description | + |------------|--------------|-------------| + | ApplicationStartup | ApplicationStarted | Application started | + | SystemThemeChanged | ThemeChanged | System theme changed | + + + +## Window Events + +Window events are triggered by window-specific actions such as resizing, moving, or changing focus state. You can listen for these events using the `OnWindowEvent` method: + +```go +window.OnWindowEvent(events.Common.WindowClosing, func(e *application.WindowEvent) { + app.Logger.Info("Window is closing!") +}) + +window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { + app.Logger.Info("Window gained focus!") +}) +``` + +### Hooks vs Standard Listeners + +Wails provides two ways to handle window events: standard listeners (OnWindowEvent) and hooks (RegisterHook). The key differences are: + +1. **Execution Order**: Hooks are executed first and in the order they are registered, while standard listeners execute after Hooks and have no guaranteed order. +2. **Blocking**: Hooks are blocking and must complete before the next hook is executed. Standard listeners are non-blocking. +3. **Event Cancellation**: When cancelling an event in a Hook, it prevents it from propagating further. This is useful to prevent +default behaviour, such as closing a window. Cancelling an event in a standard listener will only prevent it from being emitted +from that point in time. + +In this example, the window will only close after the close button has been clicked three times, demonstrating how hooks can be used to control event flow. + +```go +// Hook - runs synchronously. The window will not close until the countdown reaches zero. +var countdown = 3 +window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + countdown-- + if countdown == 0 { + app.Logger.Info("Window closing - countdown reached zero!") + return + } + app.Logger.Info("Preventing window from closing - countdown:", countdown) + e.Cancel() +}) +``` + +This next example demonstrates the execution order of hooks vs standard listeners. + +```go +window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { + app.Logger.Info("I always run after hooks!") +}) + +// Multiple hooks are executed in order +window.RegisterHook(events.Common.WindowFocus, func(e *application.WindowEvent) { + app.Logger.Info("First focus hook - will always run first!") +}) +window.RegisterHook(events.Common.WindowFocus, func(e *application.WindowEvent) { + app.Logger.Info("Second focus hook - will always run second!") +}) +``` + +This produces the following output: + +``` +INF First focus hook - will always run first! +INF Second focus hook - will always run second! +INF I always run after hooks! +``` + +### Common Window Events + +| Event Name | Description | +|--------------------|---------------------------| +| WindowClosing | Window is closing | +| WindowDidMove | Window moved | +| WindowDidResize | Window resized | +| WindowDPIChanged | Window DPI changed | +| WindowFilesDropped | Files dropped on window | +| WindowFocus | Window gained focus | +| WindowFullscreen | Window entered fullscreen | +| WindowHide | Window hidden | +| WindowLostFocus | Window lost focus | +| WindowMaximise | Window maximised | +| WindowMinimise | Window minimised | +| WindowRestore | Window restored | +| WindowRuntimeReady | Window runtime is ready | +| WindowShow | Window shown | +| WindowUnFullscreen | Window exited fullscreen | +| WindowUnMaximise | Window unmaximised | +| WindowUnMinimise | Window unminimised | +| WindowZoom | Window zoomed | +| WindowZoomIn | Window zoomed in | +| WindowZoomOut | Window zoomed out | +| WindowZoomReset | Window zoom reset | + +### Enhanced Drag and Drop with Targeted Dropzones + +Wails v3 introduces an enhanced drag-and-drop system that allows you to define specific "dropzones" within your application's HTML. This provides finer control over where files can be dropped and offers automatic visual feedback managed by the Wails runtime. + +#### 1. Defining Dropzones in HTML + +To designate an HTML element as a dropzone, add the `data-wails-dropzone` attribute to it. Any element with this attribute will become a valid target for file drops. + +**Example:** +```html +
+

Drop files here!

+
+ +
+ Another drop target +
+ + +
+ 📁 Documents +
+``` + +#### 2. Visual Feedback + +When files are dragged over an element marked with `data-wails-dropzone`, the Wails JavaScript runtime automatically adds the `wails-dropzone-hover` CSS class to that element. You can define styles for this class to provide visual feedback: + +**Example CSS:** +```css +/* Base style for all dropzones (resting state) */ +.dropzone { + border: 2px dashed #888; + background-color: #303030; + padding: 20px; + text-align: center; +} + +/* Default hover effect applied by the runtime */ +.dropzone.wails-dropzone-hover { + background-color: #3c3c3e; + border-style: dotted; + border-color: #007bff; + box-shadow: 0 0 8px rgba(0, 123, 255, 0.4); +} + +/* Example: Customizing hover for a specific dropzone to override the default */ +#myDropArea.wails-dropzone-hover { + background-color: lightgreen; + outline: 2px solid green; +} +``` +The runtime handles adding and removing the `wails-dropzone-hover` class as files are dragged in and out of the dropzone or the window. + +#### 3. Handling Drops in Go + +On the Go side, listen for the `events.Common.WindowDropZoneFilesDropped` event. This event will be emitted when files are dropped onto an element that has the `data-wails-dropzone` attribute. + +**Example Go Handler:** +```go +import ( + "fmt" + "log" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +// Assuming 'win' is your *application.WebviewWindow instance +win.OnWindowEvent(events.Common.WindowDropZoneFilesDropped, func(event *application.WindowEvent) { + droppedFiles := event.Context().DroppedFiles() + log.Printf("Files dropped: %v", droppedFiles) + + details := event.Context().DropZoneDetails() + if details != nil { + log.Printf("Dropped on Element ID: '%s'", details.ElementID) + log.Printf("Element Classes: %v", details.ClassList) + log.Printf("Drop Coordinates (relative to window): X=%d, Y=%d", details.X, details.Y) + + // Access custom data attributes from the HTML element + if folderName, exists := details.Attributes["data-folder-name"]; exists { + log.Printf("Folder name: %s", folderName) + } + if folderPath, exists := details.Attributes["data-path"]; exists { + log.Printf("Target path: %s", folderPath) + } + + // Example: Handle different dropzone types based on ElementID + switch details.ElementID { + case "documents": + log.Printf("Files dropped on Documents folder") + // Handle document uploads + case "downloads": + log.Printf("Files dropped on Downloads folder") + // Handle download folder drops + case "trash": + log.Printf("Files dropped on Trash") + // Handle file deletion + default: + log.Printf("Files dropped on unknown target: %s", details.ElementID) + } + + payload := map[string]interface{}{ + "files": droppedFiles, + "targetID": details.ElementID, + "targetClasses": details.ClassList, + "dropX": details.X, + "dropY": details.Y, + "attributes": details.Attributes, + } + application.Get().EmitEvent("frontend:FileDropInfo", payload) // Emits globally + // or win.EmitEvent("frontend:FileDropInfoForWindow", payload) // Emits to this specific window + } else { + log.Println("Drop occurred, but DropZoneDetails were nil.") + } +}) +``` +The `event.Context().DropZoneDetails()` method returns a pointer to an `application.DropZoneDetails` struct (or `nil` if details aren't available), containing: +- `ElementID string`: The id of the element dropped onto +- `ClassList []string`: The list of CSS classes of the HTML element that received the drop. +- `X int`: The X-coordinate of the drop, relative to the window's content area. +- `Y int`: The Y-coordinate of the drop, relative to the window's content area. +- `Attributes map[string]string`: A map containing all HTML attributes of the target element, allowing access to custom data attributes like `data-path`, `data-folder-name`, etc. + +The `event.Context().DroppedFiles()` method returns a `[]string` of file paths. + +For a fully runnable demonstration of these features, including multiple styled dropzones, please refer to the example located in the `v3/examples/drag-n-drop` directory within the Wails repository. + +### Platform-Specific Window Events + + + + | Event Name | Common Event | Description | + |------------|--------------|-------------| + | WindowDidBecomeKey | WindowFocus | Window became key window | + | WindowDidBecomeMain | - | Window became main window | + | WindowDidBeginSheet | - | Sheet began | + | WindowDidChangeAlpha | - | Window alpha changed | + | WindowDidChangeBackingLocation | - | Window backing location changed | + | WindowDidChangeBackingProperties | - | Window backing properties changed | + | WindowDidChangeCollectionBehavior | - | Window collection behaviour changed | + | WindowDidChangeEffectiveAppearance | - | Window appearance changed | + | WindowDidChangeOcclusionState | - | Window occlusion state changed | + | WindowDidChangeOrderingMode | - | Window ordering mode changed | + | WindowDidChangeScreen | - | Window screen changed | + | WindowDidChangeScreenParameters | - | Window screen parameters changed | + | WindowDidChangeScreenProfile | - | Window screen profile changed | + | WindowDidChangeScreenSpace | - | Window screen space changed | + | WindowDidChangeScreenSpaceProperties | - | Window screen space properties changed | + | WindowDidChangeSharingType | - | Window sharing type changed | + | WindowDidChangeSpace | - | Window space changed | + | WindowDidChangeSpaceOrderingMode | - | Window space ordering mode changed | + | WindowDidChangeTitle | - | Window title changed | + | WindowDidChangeToolbar | - | Window toolbar changed | + | WindowDidDeminiaturize | WindowUnMinimise | Window unminimised | + | WindowDidEndSheet | - | Sheet ended | + | WindowDidEnterFullScreen | WindowFullscreen | Window entered fullscreen | + | WindowDidEnterVersionBrowser | - | Window entered version browser | + | WindowDidExitFullScreen | WindowUnFullscreen | Window exited fullscreen | + | WindowDidExitVersionBrowser | - | Window exited version browser | + | WindowDidExpose | - | Window exposed | + | WindowDidFocus | WindowFocus | Window gained focus | + | WindowDidMiniaturize | WindowMinimise | Window minimised | + | WindowDidMove | WindowDidMove | Window moved | + | WindowDidOrderOffScreen | - | Window ordered off screen | + | WindowDidOrderOnScreen | - | Window ordered on screen | + | WindowDidResignKey | - | Window resigned key window | + | WindowDidResignMain | - | Window resigned main window | + | WindowDidResize | WindowDidResize | Window resized | + | WindowDidUpdate | - | Window updated | + | WindowDidUpdateAlpha | - | Window alpha updated | + | WindowDidUpdateCollectionBehavior | - | Window collection behaviour updated | + | WindowDidUpdateCollectionProperties | - | Window collection properties updated | + | WindowDidUpdateShadow | - | Window shadow updated | + | WindowDidUpdateTitle | - | Window title updated | + | WindowDidUpdateToolbar | - | Window toolbar updated | + | WindowDidZoom | WindowZoom | Window zoomed | + | WindowFileDraggingEntered | - | File dragging entered window | + | WindowFileDraggingExited | - | File dragging exited window | + | WindowFileDraggingPerformed | - | File dragging performed | + | WindowHide | WindowHide | Window hidden | + | WindowMaximise | WindowMaximise | Window maximised | + | WindowShouldClose | WindowClosing | Window should close | + | WindowShow | WindowShow | Window shown | + | WindowUnMaximize | WindowUnMaximise | Window unmaximised | + | WindowZoomIn | WindowZoomIn | Window zoomed in | + | WindowZoomOut | WindowZoomOut | Window zoomed out | + | WindowZoomReset | WindowZoomReset | Window zoom reset | + |------------|--------------|-------------| + + + + | Event Name | Common Event | Description | + |------------|--------------|-------------| + | WebViewNavigationCompleted | - | WebView navigation completed | + | WindowActive | - | Window became active | + | WindowBackgroundErase | - | Window background needs erasing | + | WindowClickActive | - | Window clicked whilst active | + | WindowClosing | WindowClosing | Window closing | + | WindowDidMove | WindowDidMove | Window moved | + | WindowDidResize | WindowDidResize | Window resized | + | WindowEndMove | - | Window finished moving | + | WindowEndResize | - | Window finished resising | + | WindowFullscreen | WindowFullscreen | Window entered fullscreen | + | WindowHide | WindowHide | Window hidden | + | WindowInactive | - | Window became inactive | + | WindowKillFocus | WindowLostFocus | Window lost focus | + | WindowMaximise | WindowMaximise | Window maximised | + | WindowMinimise | WindowMinimise | Window minimised | + | WindowPaint | - | Window needs painting | + | WindowRestore | WindowRestore | Window restored | + | WindowSetFocus | WindowFocus | Window gained focus | + | WindowShow | WindowShow | Window shown | + | WindowStartMove | - | Window started moving | + | WindowStartResize | - | Window started resising | + | WindowUnFullscreen | WindowUnFullscreen | Window exited fullscreen | + | WindowUnMaximise | WindowUnMaximise | Window unmaximised | + | WindowUnMinimise | WindowUnMinimise | Window unminimised | + | WindowZOrderChanged | - | Window z-order changed | + + #### Input Events + | Event Name | Description | + |------------|-------------| + | WindowDragDrop | Files dragged and dropped | + | WindowDragEnter | Drag entered window | + | WindowDragLeave | Drag left window | + | WindowDragOver | Drag over window | + | WindowKeyDown | Key pressed | + | WindowKeyUp | Key released | + | WindowNonClientHit | Mouse hit in non-client area | + | WindowNonClientMouseDown | Mouse down in non-client area | + | WindowNonClientMouseLeave | Mouse left non-client area | + | WindowNonClientMouseMove | Mouse move in non-client area | + | WindowNonClientMouseUp | Mouse up in non-client area | + + + + | Event Name | Common Event | Description | + |------------|--------------|-------------| + | WindowDeleteEvent | WindowClosing | Window delete requested | + | WindowDidMove | WindowDidMove | Window moved | + | WindowDidResize | WindowDidResize | Window resized | + | WindowFocusIn | WindowFocus | Window gained focus | + | WindowFocusOut | WindowLostFocus | Window lost focus | + | WindowLoadChanged | WindowShow | Window load state changed | + + + +## Menu Events + +Menu events are triggered by menu-specific actions such as opening, closing, and interacting with menu items. These events are useful for creating dynamic menus and responding to menu interactions. + +```go +// Listen for menu events +app.Event.OnApplicationEvent(events.Mac.MenuDidOpen, func(event *application.ApplicationEvent) { + app.Logger.Info("Menu opened!") +}) + +app.Event.OnApplicationEvent(events.Mac.MenuWillSendAction, func(event *application.ApplicationEvent) { + app.Logger.Info("Menu about to send action!") +}) +``` + +For more information about menus, see the [Application Menu](/learn/application-menu) and [Context Menu](/learn/context-menu) documentation. + +### Platform-Specific Menu Events + + + + | Event Name | Description | + |------------|-------------| + | MenuDidAddItem | Menu item added | + | MenuDidBeginTracking | Menu began tracking | + | MenuDidClose | Menu closed | + | MenuDidDisplayItem | Menu item displayed | + | MenuDidEndTracking | Menu ended tracking | + | MenuDidHighlightItem | Menu item highlighted | + | MenuDidOpen | Menu opened | + | MenuDidPopUp | Menu popped up | + | MenuDidRemoveItem | Menu item removed | + | MenuDidSendAction | Menu sent action | + | MenuDidSendActionToItem | Menu sent action to item | + | MenuDidUpdate | Menu updated | + | MenuWillAddItem | Menu will add item | + | MenuWillBeginTracking | Menu will begin tracking | + | MenuWillDisplayItem | Menu will display item | + | MenuWillEndTracking | Menu will end tracking | + | MenuWillHighlightItem | Menu will highlight item | + | MenuWillOpen | Menu will open | + | MenuWillPopUp | Menu will pop up | + | MenuWillRemoveItem | Menu will remove item | + | MenuWillSendAction | Menu will send action | + | MenuWillSendActionToItem | Menu will send action to item | + | MenuWillUpdate | Menu will update | + + + + Windows does not currently provide specific menu events. + + + + Linux does not currently provide specific menu events. + + + +## WebView Events + +WebView events are triggered by navigation and loading state changes in the WebView component. These events are useful for tracking page loads and navigation state. + +```go +// Listen for WebView navigation events +app.Event.OnApplicationEvent(events.Mac.WebViewDidStartProvisionalNavigation, func(event *application.ApplicationEvent) { + app.Logger.Info("WebView started loading a page!") +}) + +app.Event.OnApplicationEvent(events.Mac.WebViewDidFinishNavigation, func(event *application.ApplicationEvent) { + app.Logger.Info("WebView finished loading the page!") +}) + +// On Windows +app.Event.OnApplicationEvent(events.Windows.WebViewNavigationCompleted, func(event *application.ApplicationEvent) { + app.Logger.Info("WebView navigation completed!") +}) +``` + +### Platform-Specific WebView Events + + + + | Event Name | Description | + |------------|-------------| + | WebViewDidCommitNavigation | Navigation committed | + | WebViewDidFinishNavigation | Navigation finished | + | WebViewDidReceiveServerRedirectForProvisionalNavigation | Server redirect received | + | WebViewDidStartProvisionalNavigation | Provisional navigation started | + + + + | Event Name | Description | + |------------|-------------| + | WebViewNavigationCompleted | Navigation completed | + + + + Linux does not currently provide specific WebView events. + + + +## Custom Events + +You can emit and listen for custom events to enable communication between different parts of your application. Wails v3 provides both a traditional API and a new [Manager API](/learn/manager-api) for better organization. + +### Emitting Events + +You can emit custom events from anywhere in your application: + + + +```go +// NEW: Using the Event Manager (recommended) +app.Event.Emit("myevent", "hello") + +// Emit from a specific window +window.EmitEvent("windowevent", "window specific data") +``` + + +```go +// Traditional API (no longer works) +app.EmitEvent("myevent", "hello") + +// Emit from a specific window +window.EmitEvent("windowevent", "window specific data") +``` + + + +### Handling Custom Events + +Listen for custom events using the event management methods: + + + +```go +// NEW: Using the Event Manager (recommended) +cancelFunc := app.Event.On("myevent", func(e *application.CustomEvent) { + // Access event information + name := e.Name // Event name + data := e.Data // Event data + sender := e.Sender // Name of the window sending the event, or "" if sent from application + cancelled := e.IsCancelled() // Event cancellation status +}) + +// Remove specific event listener +app.Event.Off("myevent") + +// Remove all event listeners +app.Event.Reset() + +// Listen for events a limited number of times +app.Event.OnMultiple("myevent", func(e *application.CustomEvent) { + // This will only be called 3 times +}, 3) +``` + + +```go +// Traditional API (still supported) +cancelFunc := app.OnEvent("myevent", func(e *application.CustomEvent) { + // Access event information + name := e.Name // Event name + data := e.Data // Event data + sender := e.Sender // Name of the window sending the event, or "" if sent from application + cancelled := e.IsCancelled() // Event cancellation status +}) + +// Remove specific event listener +app.OffEvent("myevent") + +// Remove all event listeners +app.ResetEvents() + +// Listen for events a limited number of times +app.OnMultipleEvent("myevent", func(e *application.CustomEvent) { + // This will only be called 3 times +}, 3) +``` + + + +## Event Cancellation + +Events can be cancelled to prevent their default behaviour or stop propagation to other listeners. This is particularly useful for hooks that need to control window closing, menu actions, or other system events. + +### Cancelling Events + +To cancel an event, call the `Cancel()` method on the event object: + +```go +window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + // Prevent the window from closing + e.Cancel() +}) +``` + +### Checking Event Cancellation + +You can check if an event has been cancelled using the `IsCancelled()` method: + +```go +window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + if e.IsCancelled() { + app.Logger.Info("Window closing was cancelled by another hook") + return + } + // Process event +}) + +// For custom events +app.Event.On("myevent", func(e *application.CustomEvent) { + if e.IsCancelled() { + app.Logger.Info("Event was cancelled") + return + } + // Process event +}) +``` + +:::tip[Pro Tip] +Remember that event cancellation in hooks affects all subsequent hooks and listeners, whilst cancellation in standard listeners only affects listeners that haven't yet been called. +::: \ No newline at end of file diff --git a/docs/src/content/docs/learn/keybindings.mdx b/docs/src/content/docs/learn/keybindings.mdx new file mode 100644 index 000000000..0998f096e --- /dev/null +++ b/docs/src/content/docs/learn/keybindings.mdx @@ -0,0 +1,437 @@ +--- +title: Key Bindings +sidebar: + order: 56 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Wails provides a powerful key binding system that allows you to register global keyboard shortcuts that work across all windows in your application. This enables users to quickly access functionality without navigating through menus. + +## Accessing the Key Binding Manager + +The key binding manager is accessed through the `KeyBindings` property on your application instance: + +```go +app := application.New(application.Options{ + Name: "Keyboard Shortcuts Demo", +}) + +// Access the key binding manager +keyBindings := app.KeyBinding +``` + +## Adding Key Bindings + +### Basic Key Binding + +Register a simple keyboard shortcut: + +```go +app.KeyBinding.Add("Ctrl+S", func(window *application.WebviewWindow) { + // Handle save action + app.Logger.Info("Save shortcut triggered") + // Perform save operation... +}) +``` + +### Multiple Key Bindings + +Register multiple shortcuts for common operations: + +```go +// File operations +app.KeyBinding.Add("Ctrl+N", func(window *application.WebviewWindow) { + // New file + window.EmitEvent("file:new", nil) +}) + +app.KeyBinding.Add("Ctrl+O", func(window *application.WebviewWindow) { + // Open file + dialog := app.Dialog.OpenFile() + if file, err := dialog.PromptForSingleSelection(); err == nil { + window.EmitEvent("file:open", file) + } +}) + +app.KeyBinding.Add("Ctrl+S", func(window *application.WebviewWindow) { + // Save file + window.EmitEvent("file:save", nil) +}) + +// Edit operations +app.KeyBinding.Add("Ctrl+Z", func(window *application.WebviewWindow) { + // Undo + window.EmitEvent("edit:undo", nil) +}) + +app.KeyBinding.Add("Ctrl+Y", func(window *application.WebviewWindow) { + // Redo (Windows/Linux) + window.EmitEvent("edit:redo", nil) +}) + +app.KeyBinding.Add("Cmd+Shift+Z", func(window *application.WebviewWindow) { + // Redo (macOS) + window.EmitEvent("edit:redo", nil) +}) +``` + +## Key Binding Accelerators + +### Accelerator Format + +Key bindings use a standard accelerator format with modifiers and keys: + +```go +// Modifier keys +"Ctrl+S" // Control + S +"Cmd+S" // Command + S (macOS) +"Alt+F4" // Alt + F4 +"Shift+Ctrl+Z" // Shift + Control + Z + +// Function keys +"F1" // F1 key +"Ctrl+F5" // Control + F5 + +// Special keys +"Escape" // Escape key +"Enter" // Enter key +"Space" // Spacebar +"Tab" // Tab key +"Backspace" // Backspace key +"Delete" // Delete key + +// Arrow keys +"Up" // Up arrow +"Down" // Down arrow +"Left" // Left arrow +"Right" // Right arrow +``` + +### Platform-Specific Accelerators + +Handle platform differences for common shortcuts: + +```go +import "runtime" + +// Cross-platform save shortcut +if runtime.GOOS == "darwin" { + app.KeyBinding.Add("Cmd+S", saveHandler) +} else { + app.KeyBinding.Add("Ctrl+S", saveHandler) +} + +// Or register both +app.KeyBinding.Add("Ctrl+S", saveHandler) +app.KeyBinding.Add("Cmd+S", saveHandler) +``` + +## Managing Key Bindings + +### Removing Key Bindings + +Remove key bindings when they're no longer needed: + +```go +// Remove a specific key binding +app.KeyBinding.Remove("Ctrl+S") + +// Example: Temporary key binding for a modal +app.KeyBinding.Add("Escape", func(window *application.WebviewWindow) { + // Close modal + window.EmitEvent("modal:close", nil) + // Remove this temporary binding + app.KeyBinding.Remove("Escape") +}) +``` + +### Getting All Key Bindings + +Retrieve all registered key bindings: + +```go +allBindings := app.KeyBinding.GetAll() +for _, binding := range allBindings { + app.Logger.Info("Key binding", "accelerator", binding.Accelerator) +} +``` + +## Advanced Usage + +### Context-Aware Key Bindings + +Make key bindings context-aware by checking application state: + +```go +app.KeyBinding.Add("Ctrl+S", func(window *application.WebviewWindow) { + // Check current application state + if isEditMode() { + // Save document + saveDocument() + } else if isInSettings() { + // Save settings + saveSettings() + } else { + app.Logger.Info("Save not available in current context") + } +}) +``` + +### Window-Specific Actions + +Key bindings receive the active window, allowing window-specific behavior: + +```go +app.KeyBinding.Add("F11", func(window *application.WebviewWindow) { + // Toggle fullscreen for the active window + if window.Fullscreen() { + window.SetFullscreen(false) + } else { + window.SetFullscreen(true) + } +}) + +app.KeyBinding.Add("Ctrl+W", func(window *application.WebviewWindow) { + // Close the active window + window.Close() +}) +``` + +### Dynamic Key Binding Management + +Dynamically add and remove key bindings based on application state: + +```go +func enableEditMode() { + // Add edit-specific key bindings + app.KeyBinding.Add("Ctrl+B", func(window *application.WebviewWindow) { + window.EmitEvent("format:bold", nil) + }) + + app.KeyBinding.Add("Ctrl+I", func(window *application.WebviewWindow) { + window.EmitEvent("format:italic", nil) + }) + + app.KeyBinding.Add("Ctrl+U", func(window *application.WebviewWindow) { + window.EmitEvent("format:underline", nil) + }) +} + +func disableEditMode() { + // Remove edit-specific key bindings + app.KeyBinding.Remove("Ctrl+B") + app.KeyBinding.Remove("Ctrl+I") + app.KeyBinding.Remove("Ctrl+U") +} +``` + +## Platform Considerations + + + + + On macOS: + + - Use `Cmd` instead of `Ctrl` for standard shortcuts + - `Cmd+Q` is typically reserved for quitting the application + - `Cmd+H` hides the application + - `Cmd+M` minimizes windows + - Consider standard macOS keyboard shortcuts + + Common macOS patterns: + ```go + app.KeyBinding.Add("Cmd+N", newFileHandler) // New + app.KeyBinding.Add("Cmd+O", openFileHandler) // Open + app.KeyBinding.Add("Cmd+S", saveFileHandler) // Save + app.KeyBinding.Add("Cmd+Z", undoHandler) // Undo + app.KeyBinding.Add("Cmd+Shift+Z", redoHandler) // Redo + app.KeyBinding.Add("Cmd+C", copyHandler) // Copy + app.KeyBinding.Add("Cmd+V", pasteHandler) // Paste + ``` + + + + + + On Windows: + + - Use `Ctrl` for standard shortcuts + - `Alt+F4` closes applications + - `F1` typically opens help + - Consider Windows keyboard conventions + + Common Windows patterns: + ```go + app.KeyBinding.Add("Ctrl+N", newFileHandler) // New + app.KeyBinding.Add("Ctrl+O", openFileHandler) // Open + app.KeyBinding.Add("Ctrl+S", saveFileHandler) // Save + app.KeyBinding.Add("Ctrl+Z", undoHandler) // Undo + app.KeyBinding.Add("Ctrl+Y", redoHandler) // Redo + app.KeyBinding.Add("Ctrl+C", copyHandler) // Copy + app.KeyBinding.Add("Ctrl+V", pasteHandler) // Paste + app.KeyBinding.Add("F1", helpHandler) // Help + ``` + + + + + + On Linux: + + - Generally follows Windows conventions with `Ctrl` + - May vary by desktop environment + - Consider GNOME/KDE standard shortcuts + - Some desktop environments reserve certain shortcuts + + Common Linux patterns: + ```go + app.KeyBinding.Add("Ctrl+N", newFileHandler) // New + app.KeyBinding.Add("Ctrl+O", openFileHandler) // Open + app.KeyBinding.Add("Ctrl+S", saveFileHandler) // Save + app.KeyBinding.Add("Ctrl+Z", undoHandler) // Undo + app.KeyBinding.Add("Ctrl+Shift+Z", redoHandler) // Redo + app.KeyBinding.Add("Ctrl+C", copyHandler) // Copy + app.KeyBinding.Add("Ctrl+V", pasteHandler) // Paste + ``` + + + + +## Best Practices + +1. **Use Standard Shortcuts**: Follow platform conventions for common operations: + ```go + // Cross-platform save + if runtime.GOOS == "darwin" { + app.KeyBinding.Add("Cmd+S", saveHandler) + } else { + app.KeyBinding.Add("Ctrl+S", saveHandler) + } + ``` + +2. **Provide Visual Feedback**: Let users know when shortcuts are triggered: + ```go + app.KeyBinding.Add("Ctrl+S", func(window *application.WebviewWindow) { + saveDocument() + // Show brief notification + window.EmitEvent("notification:show", "Document saved") + }) + ``` + +3. **Handle Conflicts**: Be careful not to override important system shortcuts: + ```go + // Avoid overriding system shortcuts like: + // Ctrl+Alt+Del (Windows) + // Cmd+Space (macOS Spotlight) + // Alt+Tab (Window switching) + ``` + +4. **Document Shortcuts**: Provide help or documentation for available shortcuts: + ```go + app.KeyBinding.Add("F1", func(window *application.WebviewWindow) { + // Show help dialog with available shortcuts + showKeyboardShortcutsHelp() + }) + ``` + +5. **Clean Up**: Remove temporary key bindings when they're no longer needed: + ```go + func enterEditMode() { + app.KeyBinding.Add("Escape", exitEditModeHandler) + } + + func exitEditModeHandler(window *application.WebviewWindow) { + exitEditMode() + app.KeyBinding.Remove("Escape") // Clean up temporary binding + } + ``` + +## Complete Example + +Here's a complete example of a text editor with keyboard shortcuts: + +```go +package main + +import ( + "runtime" + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Text Editor with Shortcuts", + }) + + // File operations + if runtime.GOOS == "darwin" { + app.KeyBinding.Add("Cmd+N", func(window *application.WebviewWindow) { + window.EmitEvent("file:new", nil) + }) + app.KeyBinding.Add("Cmd+O", func(window *application.WebviewWindow) { + openFile(app, window) + }) + app.KeyBinding.Add("Cmd+S", func(window *application.WebviewWindow) { + window.EmitEvent("file:save", nil) + }) + } else { + app.KeyBinding.Add("Ctrl+N", func(window *application.WebviewWindow) { + window.EmitEvent("file:new", nil) + }) + app.KeyBinding.Add("Ctrl+O", func(window *application.WebviewWindow) { + openFile(app, window) + }) + app.KeyBinding.Add("Ctrl+S", func(window *application.WebviewWindow) { + window.EmitEvent("file:save", nil) + }) + } + + // View operations + app.KeyBinding.Add("F11", func(window *application.WebviewWindow) { + window.SetFullscreen(!window.Fullscreen()) + }) + + app.KeyBinding.Add("F1", func(window *application.WebviewWindow) { + showKeyboardShortcuts(window) + }) + + // Create main window + window := app.Window.New() + window.SetTitle("Text Editor") + + err := app.Run() + if err != nil { + panic(err) + } +} + +func openFile(app *application.App, window *application.WebviewWindow) { + dialog := app.Dialog.OpenFile() + dialog.AddFilter("Text Files", "*.txt;*.md") + + if file, err := dialog.PromptForSingleSelection(); err == nil { + window.EmitEvent("file:open", file) + } +} + +func showKeyboardShortcuts(window *application.WebviewWindow) { + shortcuts := ` +Keyboard Shortcuts: +- Ctrl/Cmd+N: New file +- Ctrl/Cmd+O: Open file +- Ctrl/Cmd+S: Save file +- F11: Toggle fullscreen +- F1: Show this help +` + window.EmitEvent("help:show", shortcuts) +} +``` + +:::tip[Pro Tip] +Test your key bindings on all target platforms to ensure they work correctly and don't conflict with system shortcuts. +::: + +:::danger[Warning] +Be careful not to override critical system shortcuts. Some key combinations are reserved by the operating system and cannot be captured by applications. +::: \ No newline at end of file diff --git a/docs/src/content/docs/learn/manager-api.mdx b/docs/src/content/docs/learn/manager-api.mdx new file mode 100644 index 000000000..d58445742 --- /dev/null +++ b/docs/src/content/docs/learn/manager-api.mdx @@ -0,0 +1,265 @@ +--- +title: Manager API +sidebar: + order: 25 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +The Wails v3 Manager API provides an organized and discoverable way to access application functionality through focused manager structs. This new API structure groups related methods together while maintaining full backward compatibility with the traditional App API. + +## Overview + +The Manager API organizes application functionality into eleven focused areas: + +- **`app.Window`** - Window creation, management, and callbacks +- **`app.ContextMenu`** - Context menu registration and management +- **`app.KeyBinding`** - Global key binding management +- **`app.Browser`** - Browser integration (opening URLs and files) +- **`app.Env`** - Environment information and system state +- **`app.Dialog`** - File and message dialog operations +- **`app.Event`** - Custom event handling and application events +- **`app.Menu`** - Application menu management +- **`app.Screen`** - Screen management and coordinate transformations +- **`app.Clipboard`** - Clipboard text operations +- **`app.SystemTray`** - System tray icon creation and management + +## Benefits + +- **Better discoverability** - IDE autocomplete shows organized API surface +- **Improved code organization** - Related methods grouped together +- **Enhanced maintainability** - Separation of concerns across managers +- **Future extensibility** - Easier to add new features to specific areas + +## Usage + +The Manager API provides organized access to all application functionality: + +```go +// Events and custom event handling +app.Event.Emit("custom", data) +app.Event.On("custom", func(e *CustomEvent) { ... }) + +// Window management +window, _ := app.Window.GetByName("main") +app.Window.OnCreate(func(window Window) { ... }) + +// Browser integration +app.Browser.OpenURL("https://wails.io") + +// Menu management +menu := app.Menu.New() +app.Menu.Set(menu) + +// System tray +systray := app.SystemTray.New() +``` + +## Manager Reference + +### Window Manager + +Manages window creation, retrieval, and lifecycle callbacks. + +```go +// Create windows +window := app.Window.New() +window := app.Window.NewWithOptions(options) +current := app.Window.Current() + +// Find windows +window, exists := app.Window.GetByName("main") +windows := app.Window.GetAll() + +// Window callbacks +app.Window.OnCreate(func(window Window) { + // Handle window creation +}) +``` + +### Event Manager + +Handles custom events and application event listening. + +```go +// Custom events +app.Event.Emit("userAction", data) +cancelFunc := app.Event.On("userAction", func(e *CustomEvent) { + // Handle event +}) +app.Event.Off("userAction") +app.Event.Reset() // Remove all listeners + +// Application events +app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(e *ApplicationEvent) { + // Handle system theme change +}) +``` + +### Browser Manager + +Provides browser integration for opening URLs and files. + +```go +// Open URLs and files in default browser +err := app.Browser.OpenURL("https://wails.io") +err := app.Browser.OpenFile("/path/to/document.pdf") +``` + +### Environment Manager + +Access to system environment information. + +```go +// Get environment info +env := app.Env.Info() +fmt.Printf("OS: %s, Arch: %s\n", env.OS, env.Arch) + +// Check system theme +if app.Env.IsDarkMode() { + // Dark mode is active +} + +// Open file manager +err := app.Env.OpenFileManager("/path/to/folder", false) +``` + +### Dialog Manager + +Organized access to file and message dialogs. + +```go +// File dialogs +result, err := app.Dialog.OpenFile(). + AddFilter("Text Files", "*.txt"). + PromptForSingleSelection() + +result, err = app.Dialog.SaveFile(). + SetDefaultFilename("document.txt"). + PromptForSingleSelection() + +// Message dialogs +app.Dialog.Info(). + SetTitle("Information"). + SetMessage("Operation completed successfully"). + Show() + +app.Dialog.Error(). + SetTitle("Error"). + SetMessage("An error occurred"). + Show() +``` + +### Menu Manager + +Application menu creation and management. + +```go +// Create and set application menu +menu := app.Menu.New() +fileMenu := menu.AddSubmenu("File") +fileMenu.Add("New").OnClick(func(ctx *Context) { + // Handle menu click +}) + +app.Menu.Set(menu) + +// Show about dialog +app.Menu.ShowAbout() +``` + +### Key Binding Manager + +Dynamic management of global key bindings. + +```go +// Add key bindings +app.KeyBinding.Add("ctrl+n", func(window *WebviewWindow) { + // Handle Ctrl+N +}) + +app.KeyBinding.Add("ctrl+q", func(window *WebviewWindow) { + app.Quit() +}) + +// Remove key bindings +app.KeyBinding.Remove("ctrl+n") + +// Get all bindings +bindings := app.KeyBinding.GetAll() +``` + +### Context Menu Manager + +Advanced context menu management (for library authors). + +```go +// Create and register context menu +menu := app.ContextMenu.New() +app.ContextMenu.Add("myMenu", menu) + +// Retrieve context menu +menu, exists := app.ContextMenu.Get("myMenu") + +// Remove context menu +app.ContextMenu.Remove("myMenu") +``` + +### Screen Manager + +Screen management and coordinate transformations for multi-monitor setups. + +```go +// Get screen information +screens := app.Screen.GetAll() +primary := app.Screen.GetPrimary() + +// Coordinate transformations +physicalPoint := app.Screen.DipToPhysicalPoint(logicalPoint) +logicalPoint := app.Screen.PhysicalToDipPoint(physicalPoint) + +// Screen detection +screen := app.Screen.ScreenNearestDipPoint(point) +screen = app.Screen.ScreenNearestDipRect(rect) +``` + +### Clipboard Manager + +Clipboard operations for reading and writing text. + +```go +// Set text to clipboard +success := app.Clipboard.SetText("Hello World") +if !success { + // Handle error +} + +// Get text from clipboard +text, ok := app.Clipboard.Text() +if !ok { + // Handle error +} else { + // Use the text +} +``` + +### SystemTray Manager + +System tray icon creation and management. + +```go +// Create system tray +systray := app.SystemTray.New() +systray.SetLabel("My App") +systray.SetIcon(iconBytes) + +// Add menu to system tray +menu := app.Menu.New() +menu.Add("Open").OnClick(func(ctx *Context) { + // Handle click +}) +systray.SetMenu(menu) + +// Destroy system tray when done +systray.Destroy() +``` \ No newline at end of file diff --git a/docs/src/content/docs/learn/menu-reference.mdx b/docs/src/content/docs/learn/menu-reference.mdx new file mode 100644 index 000000000..b0659f061 --- /dev/null +++ b/docs/src/content/docs/learn/menu-reference.mdx @@ -0,0 +1,188 @@ +--- +title: Menu Reference +sidebar: + order: 52 +--- + +This reference document covers the common menu item types and properties available in Wails v3. These features are shared between application menus and context menus. + +## Menu Item Types + +### Regular Menu Items + +The most basic type of menu item displays text and triggers an action when clicked: + +```go +menuItem := menu.Add("Click Me") +menuItem.OnClick(func(ctx *application.Context) { + // Handle click +}) +``` + +### Checkboxes + +Checkbox menu items provide a toggleable state: + +```go +checkbox := menu.AddCheckbox("Enable Feature", true) // true = initially checked +checkbox.OnClick(func(ctx *application.Context) { + isChecked := ctx.ClickedMenuItem().Checked() + // Handle state change +}) +``` + +### Radio Groups + +Radio items create mutually exclusive options. Items are grouped automatically when placed adjacently: + +```go +menu.AddRadio("Option 1", true) // true = initially selected +menu.AddRadio("Option 2", false) +menu.AddRadio("Option 3", false) +``` + +### Submenus + +Submenus allow you to create nested menu structures: + +```go +submenu := menu.AddSubmenu("More Options") +submenu.Add("Submenu Item 1") +submenu.Add("Submenu Item 2") +``` + +### Separators + +Separators add visual dividers between menu items: + +```go +menu.Add("Item 1") +menu.AddSeparator() +menu.Add("Item 2") +``` + +## Menu Item Properties + +### Label + +The text displayed for the menu item: + +```go +menuItem := menu.Add("Initial Label") +menuItem.SetLabel("New Label") +``` + +### Enabled State + +Control whether the menu item can be interacted with: + +```go +menuItem := menu.Add("Disabled Item") +menuItem.SetEnabled(false) +``` + +### Checked State + +For checkbox and radio items, control or query their checked state: + +```go +checkbox := menu.AddCheckbox("Feature", false) +checkbox.SetChecked(true) +isChecked := checkbox.Checked() +``` + +### Accelerators + +Add keyboard shortcuts to menu items: + +```go +menuItem := menu.Add("Save") +menuItem.SetAccelerator("CmdOrCtrl+S") +``` + +Common accelerator modifiers: +- `CmdOrCtrl`: Command on macOS, Control on Windows/Linux +- `Shift` +- `Alt`: Option on macOS +- `Ctrl`: Control key on all platforms + +## Event Handling + +### Click Events + +Handle menu item clicks using the `OnClick` method: + +```go +menuItem.OnClick(func(ctx *application.Context) { + // Access the clicked item + clickedItem := ctx.ClickedMenuItem() + + // Get current state + label := clickedItem.Label() + isChecked := clickedItem.Checked() + + // Update the item + clickedItem.SetLabel("New Label") +}) +``` + +### Shared Event Handlers + +Event handlers can be shared amongst multiple menu items: + +```go +handleClick := func(ctx *application.Context) { + item := ctx.ClickedMenuItem() + // Common handling logic +} + +menu.Add("Item 1").OnClick(handleClick) +menu.Add("Item 2").OnClick(handleClick) +``` + +## Dynamic Updates + +Menu items can be updated dynamically during runtime: + +```go +menuItem := menu.Add("Initial State") + +// Later, update the item +menuItem.SetLabel("New Label") +menuItem.SetEnabled(false) +menuItem.SetChecked(true) + +// Apply changes +menu.Update() +``` + +:::note[Update Required] +After modifying menu items, call `Update()` on the parent menu to apply the changes. +::: + +## Best Practices + +1. Use clear, concise labels that describe the action +2. Group related items together using separators +3. Limit submenu depth to maintain usability +4. Provide keyboard shortcuts for common actions +5. Keep radio groups focused on a single choice +6. Update menu items to reflect application state +7. Handle all possible states in click handlers + +:::tip[Pro Tip] +When sharing event handlers, use the `ctx.ClickedMenuItem()` method to determine which item triggered the event and handle it accordingly. +::: + +## Platform Considerations + +:::note[Platform Behaviour] +Menu appearance and behaviour varies by platform: +- macOS: Uses native menu styling and supports system roles +- Windows: Follows Windows menu conventions +- Linux: Adapts to the desktop environment's theme +::: + +:::danger[Warning] +Always test menu functionality across all supported platforms, as behaviour and appearance may vary significantly. +::: diff --git a/docs/src/content/docs/learn/notifications.mdx b/docs/src/content/docs/learn/notifications.mdx new file mode 100644 index 000000000..8cb63646b --- /dev/null +++ b/docs/src/content/docs/learn/notifications.mdx @@ -0,0 +1,304 @@ +--- +title: Notifications +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +## Introduction + +Wails provides a comprehensive cross-platform notification system for desktop applications. This service allows you to display native system notifications, with support for interactive elements like action buttons and text input fields. + +## Basic Usage + +### Creating the Service + +First, initialize the notifications service: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/services/notifications" + +// Create a new notification service +notifier := notifications.New() + +//Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(notifier), + }, +}) +``` + +## Notification Authorization + +Notifications on macOS require user authorization. Request and check authorization: + +```go +authorized, err := notifier.CheckNotificationAuthorization() +if err != nil { + // Handle authorization error +} +if authorized { + // Send notifications +} else { + // Request authorization + authorized, err = notifier.RequestNotificationAuthorization() +} +``` +On Windows and Linux this always returns `true`. + +## Notification Types + +### Basic Notifications + +Send a basic notification with a unique id, title, optional subtitle (macOS and Linux), and body text to users: + +```go +notifier.SendNotification(notifications.NotificationOptions{ + ID: "unique-id", + Title: "New Calendar Invite", + Subtitle: "From: Jane Doe", // Optional + Body: "Tap to view the event", +}) + +``` + +### Interactive Notifications +Send a notification with action buttons and text inputs. These notifications require a notification category to be resgistered first: + +```go +// Define a unique category id +categoryID := "unique-category-id" + +// Define a category with actions +category := notifications.NotificationCategory{ + ID: categoryID, + Actions: []notifications.NotificationAction{ + { + ID: "OPEN", + Title: "Open", + }, + { + ID: "ARCHIVE", + Title: "Archive", + Destructive: true, /* macOS-specific */ + }, + }, + HasReplyField: true, + ReplyPlaceholder: "message...", + ReplyButtonTitle: "Reply", +} + +// Register the category +notifier.RegisterNotificationCategory(category) + +// Send an interactive notification with the actions registered in the provided category +notifier.SendNotificationWithActions(notifications.NotificationOptions{ + ID: "unique-id", + Title: "New Message", + Subtitle: "From: Jane Doe", + Body: "Are you able to make it?", + CategoryID: categoryID, +}) +``` + +## Notification Responses + +Process user interactions with notifications: + +```go +notifier.OnNotificationResponse(func(result notifications.NotificationResult) { + response := result.Response + fmt.Printf("Notification %s was actioned with: %s\n", response.ID, response.ActionIdentifier) + + if response.ActionIdentifier == "TEXT_REPLY" { + fmt.Printf("User replied: %s\n", response.UserText) + } + + if data, ok := response.UserInfo["sender"].(string); ok { + fmt.Printf("Original sender: %s\n", data) + } + + // Emit an event to the frontend + app.Event.Emit("notification", result.Response) +}) +``` + +## Notification Customisation + +### Custom Metadata + +Basic and interactive notifications can include custom data: + +```go +notifier.SendNotification(notifications.NotificationOptions{ + ID: "unique-id", + Title: "New Calendar Invite", + Subtitle: "From: Jane Doe", // Optional + Body: "Tap to view the event", + Data: map[string]interface{}{ + "sender": "jane.doe@example.com", + "timestamp": "2025-03-10T15:30:00Z", + } +}) + +``` + +## Platform Considerations + + + + + On macOS, notifications: + + - Require user authorization + - Require the app to be notorized for distribution + - Use system-standard notification appearances + - Support `subtitle` + - Support user text input + - Support the `Destructive` action option + - Automatically handle dark/light mode + + + + + + On Windows, notifications: + + - Use Windows system toast styles + - Adapt to Windows theme settings + - Support user text input + - Support high DPI displays + - Do not support `subtitle` + + + + + + On Linux, dialog behaviour depends on the desktop environment: + + - Use native notifications when available + - Follow desktop environment theme + - Position according to desktop environment rules + - Support `subtitle` + - Do not support user text input + + + + +## Best Practices + +1. Check and request for authorization: + - macOS requires user authorization + +2. Provide clear and concise notifications: + - Use descriptive titles, subtitles, text, and action titles + +3. Handle dialog responses appropriately: + - Check for errors in notification responses + - Provide feedback for user actions + +4. Consider platform conventions: + - Follow platform-specific notification patterns + - Respect system settings + +## Examples + +Explore this example: + +- [Notifications](/examples/notifications) + +## API Reference + +### Service Management +| Method | Description | +|--------------------------------------------|-------------------------------------------------------| +| `New()` | Creates a new notifications service | + +### Notification Authorization +| Method | Description | +|----------------------------------------------|------------------------------------------------------------| +| `RequestNotificationAuthorization()` | Requests permission to display notifications (macOS) | +| `CheckNotificationAuthorization()` | Checks current notification authorization status (macOS) | + +### Sending Notifications +| Method | Description | +|------------------------------------------------------------|---------------------------------------------------| +| `SendNotification(options NotificationOptions)` | Sends a basic notification | +| `SendNotificationWithActions(options NotificationOptions)` | Sends an interactive notification with actions | + +### Notification Categories +| Method | Description | +|---------------------------------------------------------------|---------------------------------------------------| +| `RegisterNotificationCategory(category NotificationCategory)` | Registers a reusable notification category | +| `RemoveNotificationCategory(categoryID string)` | Removes a previously registered category | + +### Managing Notifications +| Method | Description | +|-------------------------------------------------|---------------------------------------------------------------------| +| `RemoveAllPendingNotifications()` | Removes all pending notifications (macOS and Linux only) | +| `RemovePendingNotification(identifier string)` | Removes a specific pending notification (macOS and Linux only) | +| `RemoveAllDeliveredNotifications()` | Removes all delivered notifications (macOS and Linux only) | +| `RemoveDeliveredNotification(identifier string)`| Removes a specific delivered notification (macOS and Linux only) | +| `RemoveNotification(identifier string)` | Removes a notification (Linux-specific) | + +### Event Handling +| Method | Description | +|--------------------------------------------------------------------|-------------------------------------------------| +| `OnNotificationResponse(callback func(result NotificationResult))` | Registers a callback for notification responses | + +### Structs and Types + +#### NotificationOptions +```go +type NotificationOptions struct { + ID string // Unique identifier for the notification + Title string // Main notification title + Subtitle string // Subtitle text (macOS and Linux only) + Body string // Main notification content + CategoryID string // Category identifier for interactive notifications + Data map[string]interface{} // Custom data to associate with the notification +} +``` + +#### NotificationCategory +```go +type NotificationCategory struct { + ID string // Unique identifier for the category + Actions []NotificationAction // Button actions for the notification + HasReplyField bool // Whether to include a text input field + ReplyPlaceholder string // Placeholder text for the input field + ReplyButtonTitle string // Text for the reply button +} +``` + +#### NotificationAction +```go +type NotificationAction struct { + ID string // Unique identifier for the action + Title string // Button text + Destructive bool // Whether the action is destructive (macOS-specific) +} +``` + +#### NotificationResponse +```go +type NotificationResponse struct { + ID string // Notification identifier + ActionIdentifier string // Action that was triggered + CategoryID string // Category of the notification + Title string // Title of the notification + Subtitle string // Subtitle of the notification + Body string // Body text of the notification + UserText string // Text entered by the user + UserInfo map[string]interface{} // Custom data from the notification +} +``` + +#### NotificationResult +```go +type NotificationResult struct { + Response NotificationResponse // Response data + Error error // Any error that occurred +} +``` \ No newline at end of file diff --git a/docs/src/content/docs/learn/runtime.mdx b/docs/src/content/docs/learn/runtime.mdx new file mode 100644 index 000000000..7b66a06b3 --- /dev/null +++ b/docs/src/content/docs/learn/runtime.mdx @@ -0,0 +1,91 @@ +--- +title: Runtime +sidebar: + order: 30 +--- + +The Wails runtime is the standard library for Wails applications. It provides a +number of features that may be used in your applications, including: + +- Window management +- Dialogs +- Browser integration +- Clipboard +- Menus +- System information +- Events +- Context Menus +- Screens +- WML (Wails Markup Language) + +The runtime is required for integration between Go and the frontend. There are 2 +ways to integrate the runtime: + +- Using the `@wailsio/runtime` package +- Using a pre-built bundle + +## Using the npm package + +The `@wailsio/runtime` package is a JavaScript package that provides access to +the Wails runtime from the frontend. It is used by all standard templates +and is the recommended way to integrate the runtime into your application. +By using the `@wailsio/runtime` package, you will only include the parts of the runtime that you use. + +The package is available on npm and can be installed using: + +```shell +npm install --save @wailsio/runtime +``` + +## Using a pre-built bundle + +Some projects will not use a Javascript bundler and may prefer to use a +pre-built bundled version of the runtime. This version can be generated locally +using the following command: + +```shell +wails3 generate runtime +``` + +The command will output a `runtime.js` (and `runtime.debug.js`) file in the current +directory. This file is an ES module that can be imported by your application scripts +just like the npm package, but the API is also exported to the global window object, +so for simpler applications you can use it as follows: + +```html + + + + + + + +``` + +:::caution +It is important to include the `type="module"` attribute on the ` + + + ``` + + Run `wails3 dev` to start the dev server. After a few seconds, the application should open. + + Type in some text and click the "Generate QR Code" button. You should see a QR code in the center of the page: + + QR Code + +
+
+ +7. ## Alternative Approach + + So far, we have covered the following areas: + - Creating a new Service + - Generating Bindings + - Using the Bindings in our Frontend code + + If the aim of your service is to serve files/assets/media to the frontend, like a traditional web server, + then there is an alternative approach to achieve the same result. + + If your service defines Go's standard http handler function `ServeHTTP(w http.ResponseWriter, r *http.Request)`, + then it can be made accessible on the frontend. Let's extend our QR code service to do this: + + ```go title="qrservice.go" ins={4-5,37-65} + package main + + import ( + "net/http" + "strconv" + + "github.com/skip2/go-qrcode" + ) + + // QRService handles QR code generation + type QRService struct { + // We can add state here if needed + } + + // NewQRService creates a new QR service + func NewQRService() *QRService { + return &QRService{} + } + + // Generate creates a QR code from the given text + func (s *QRService) Generate(text string, size int) ([]byte, error) { + // Generate the QR code + qr, err := qrcode.New(text, qrcode.Medium) + if err != nil { + return nil, err + } + + // Convert to PNG + png, err := qr.PNG(size) + if err != nil { + return nil, err + } + + return png, nil + } + + func (s *QRService) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Extract the text parameter from the request + text := r.URL.Query().Get("text") + if text == "" { + http.Error(w, "Missing 'text' parameter", http.StatusBadRequest) + return + } + // Extract Size parameter from the request + sizeText := r.URL.Query().Get("size") + if sizeText == "" { + sizeText = "256" + } + size, err := strconv.Atoi(sizeText) + if err != nil { + http.Error(w, "Invalid 'size' parameter", http.StatusBadRequest) + return + } + + // Generate the QR code + qrCodeData, err := s.Generate(text, size) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Write the QR code data to the response + w.Header().Set("Content-Type", "image/png") + w.Write(qrCodeData) + } + ``` + + Now update `main.go` to specify the route that the QR code service should be accessible on: + + ```go title="main.go" ins={8-10} + func main() { + + app := application.New(application.Options{ + Name: "myproject", + Description: "A demo of using raw HTML & CSS", + LogLevel: slog.LevelDebug, + Services: []application.Service{ + application.NewService(NewQRService(), application.ServiceOptions{ + Route: "/qrservice", + }), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "myproject", + Width: 600, + Height: 400, + }) + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } + } + ``` + + :::note + If you do not set the `Route` option explicitly, + the HTTP handler won't be accessible from the frontend. + ::: + + Finally, update `main.js` to make the image source the path to the QR code service, passing the text as a query parameter: + + ```js title="frontend/src/main.js" + async function generateQR() { + const text = document.getElementById('text').value; + if (!text) { + alert('Please enter some text'); + return; + } + + const img = document.getElementById('qrcode'); + // Make the image source the path to the QR code service, passing the text + img.src = `/qrservice?text=${encodeURIComponent(text)}` + } + + export function initializeQRGenerator() { + const button = document.getElementById('generateButton'); + if (button) { + button.addEventListener('click', generateQR); + } else { + console.error('Generate button not found'); + } + } + ``` + + Running the application again should result in the same QR code: + + QR Code +
+ +8. ## Supporting dynamic configurations + + In the example above we used a hardcoded route `/qrservice`. + If you edit `main.go` and change the `Route` option without updating `main.js`, + the application will break: + + ```go title="main.go" ins={3} + // ... + application.NewService(NewQRService(), application.ServiceOptions{ + Route: "/services/qr", + }), + // ... + ``` + + Hardcoded routes can be good for many applications, + but if you need more flexibility, method bindings and HTTP handlers + can work together to improve the development experience. + + The `ServiceStartup` Lifecycle method provides access to service options at startup, + and a custom method can be used to announce the configured route to the frontend. + + First, implement the `ServiceStartup` interface and add a new `URL` method: + + ```go title="qrservice.go" ins={4,6,10,15,23-27,46-55} + package main + + import ( + "context" + "net/http" + "net/url" + "strconv" + + "github.com/skip2/go-qrcode" + "github.com/wailsapp/wails/v3/pkg/application" + ) + + // QRService handles QR code generation + type QRService struct { + route string + } + + // NewQRService creates a new QR service + func NewQRService() *QRService { + return &QRService{} + } + + // ServiceStartup runs at application startup. + func (s *QRService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + s.route = options.Route + return nil + } + + // Generate creates a QR code from the given text + func (s *QRService) Generate(text string, size int) ([]byte, error) { + // Generate the QR code + qr, err := qrcode.New(text, qrcode.Medium) + if err != nil { + return nil, err + } + + // Convert to PNG + png, err := qr.PNG(size) + if err != nil { + return nil, err + } + + return png, nil + } + + // URL returns an URL that may be used to fetch + // a QR code with the given text and size. + // It returns an error if the HTTP handler is not available. + func (s *QRService) URL(text string, size int) (string, error) { + if s.route == "" { + return "", errors.New("http handler unavailable") + } + + return fmt.Sprintf("%s?text=%s&size=%d", s.route, url.QueryEscape(text), size), nil + } + + func (s *QRService) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Extract the text parameter from the request + text := r.URL.Query().Get("text") + if text == "" { + http.Error(w, "Missing 'text' parameter", http.StatusBadRequest) + return + } + // Extract Size parameter from the request + sizeText := r.URL.Query().Get("size") + if sizeText == "" { + sizeText = "256" + } + size, err := strconv.Atoi(sizeText) + if err != nil { + http.Error(w, "Invalid 'size' parameter", http.StatusBadRequest) + return + } + + // Generate the QR code + qrCodeData, err := s.Generate(text, size) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Write the QR code data to the response + w.Header().Set("Content-Type", "image/png") + w.Write(qrCodeData) + } + ``` + + Now update `main.js` to use the `URL` method in place of a hardcoded path: + + ```js title="frontend/src/main.js" ins={1,11-12} + import { QRService } from "./bindings/changeme"; + + async function generateQR() { + const text = document.getElementById('text').value; + if (!text) { + alert('Please enter some text'); + return; + } + + const img = document.getElementById('qrcode'); + // Invoke the URL method to obtain an URL for the given text. + img.src = await QRService.URL(text, 256); + } + + export function initializeQRGenerator() { + const button = document.getElementById('generateButton'); + if (button) { + button.addEventListener('click', generateQR); + } else { + console.error('Generate button not found'); + } + } + ``` + + It should work just like the previous example, + but changing the service route in `main.go` + will not break the frontend anymore. + + :::note + If a Go method returns a non-nil error, + the promise on the JS side will reject + and await statements will throw an exception. + ::: +
+ + diff --git a/docs/src/content/docs/whats-new.md b/docs/src/content/docs/whats-new.md new file mode 100644 index 000000000..20ada58da --- /dev/null +++ b/docs/src/content/docs/whats-new.md @@ -0,0 +1,407 @@ +--- +title: What's New in Wails v3 +--- + +Wails v3 introduces significant changes from v2. It replaces the +single-window, declarative API with a more flexible procedural approach. This +new API design improves code readability and simplifies development, especially +for complex multi-window applications. + +Wails v3 represents a substantial evolution in how desktop applications +can be built using Go and web technologies. + +## Multiple Windows + +Wails v3 introduces the ability to create and manage multiple windows within a +single application. This feature allows developers to design more complex and +versatile user interfaces, moving beyond the limitations of single-window +applications. + +Each window can be independently configured, providing flexibility in terms of +size, position, content, and behavior. This enables the creation of applications +with separate windows for different functionalities, such as main interfaces, +settings panels, or auxiliary views. + +Developers can create, manipulate, and manage these windows programmatically, +allowing for dynamic user interfaces that adapt to user needs and application +states. + +:::tip[Multiple Windows] +
Example + +```go +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Multi Window Demo", + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + }) + + window1 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + }) + + window2 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 2", + }) + + // load the embedded html from the embed.FS + window1.SetURL("/") + window1.Center() + + // Load an external URL + window2.SetURL("https://wails.app") + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} +``` +
+ +::: + +## System Tray Integration + +Wails v3 introduces robust support for system tray functionality, allowing your +application to maintain a persistent presence on the user's desktop. This +feature is particularly useful for applications that need to run in the +background or provide quick access to key functions. + +Key features of the Wails v3 system tray integration include: + +1. Window Attachment: You can associate a window with the system tray icon. When + activated, this window will be centered relative to the icon's position, + providing a great way to quickly access your application. + +2. Comprehensive Menu Support: Create rich, interactive menus that users can + access directly from the system tray icon. This allows for quick actions + without needing to open the full application window. + +3. Adaptive Icon Display: Support for both light and dark mode icons ensures + your application's system tray icon remains visible and aesthetically + pleasing across different system themes. Template icons are also supported on macOS. + +:::tip[Systray] + +
Example + + +```go +package main + +import ( + _ "embed" + "log" + "runtime" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/icons" +) + +func main() { + app := application.New(application.Options{ + Name: "Systray Demo", + Mac: application.MacOptions{ + ActivationPolicy: application.ActivationPolicyAccessory, + }, + }) + + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 500, + Height: 800, + Frameless: true, + AlwaysOnTop: true, + Hidden: true, + Windows: application.WindowsWindow{ + HiddenOnTaskbar: true, + }, + }) + + systemTray := app.SystemTray.New() + + // Support for template icons on macOS + if runtime.GOOS == "darwin" { + systemTray.SetTemplateIcon(icons.SystrayMacTemplate) + } else { + // Support for light/dark mode icons + systemTray.SetDarkModeIcon(icons.SystrayDark) + systemTray.SetIcon(icons.SystrayLight) + } + + // Support for menu + myMenu := app.Menu.New() + myMenu.Add("Hello World!").OnClick(func(_ *application.Context) { + println("Hello World!") + }) + systemTray.SetMenu(myMenu) + + // This will center the window to the systray icon with a 5px offset + // It will automatically be shown when the systray icon is clicked + // and hidden when the window loses focus + systemTray.AttachWindow(window).WindowOffset(5) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} +``` +
+::: + +## Improved bindings generation + +Wails v3 introduces a significant improvement in how bindings are generated for +your project. Bindings are the glue that connects your Go backend to your +frontend, allowing seamless communication between the two. + +Binding generation is now done using a sophisticated static analyzer that +radically improves the binding generation process. It offers enhanced speed and +preserves code quality by maintaining comments and parameter names. + +The binding generation process has been simplified, requiring only a single +command: `wails3 generate bindings`. + +:::tip[Bindings] + +
Example + +```js +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import { main } from "./models"; + +window.go = window.go || {}; +window.go.main = { + GreetService: { + /** + * GreetService.Greet + * Greet greets a person + * @param name {string} + * @returns {Promise} + **/ + Greet: function (name) { + wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); + }, + + /** + * GreetService.GreetPerson + * GreetPerson greets a person + * @param person {main.Person} + * @returns {Promise} + **/ + GreetPerson: function (person) { + wails.CallByID(4021313248, ...Array.prototype.slice.call(arguments, 0)); + }, + }, +}; +``` + +
+ +::: + +## Improved build system + +Wails v3 introduces a more flexible and transparent build system, addressing the +limitations of its predecessor. In v2, the build process was largely opaque and +difficult to customise, which could be frustrating for developers seeking more +control over their project's build process. + +All the heavy lifting that the v2 build system did, such as icon generation and +manifest creation, have been added as tool commands in the CLI. We have +incorporated [Taskfile](https://taskfile.dev) into the CLI to orchestrate these +calls to bring the same developer experience as v2. However, this approach +brings the ultimate balance of flexibility and ease of use as you can now +customise the build process to your needs. + +You can even use make if that's your thing! + +:::tip[Taskfile.yml] + +
Example + +```yaml "Snippet from Taskfile.yml" +build:darwin: + summary: Builds the application + platforms: + - darwin + cmds: + - task: pre-build + - task: build-frontend + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + - task: post-build + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" +``` +
+ +::: + +## Improved events + +Wails now emits events for various runtime operations and system activities. +This allows your application to respond to these events in real-time. +Additionally, cross-platform (common) events are available, enabling you to +write consistent event handling methods that work across different operating +systems. + +Event hooks can be registered to handle specific events synchronously. Unlike +the `On` method, these hooks allow you to cancel the event if needed. A common +use case is displaying a confirmation dialog before closing a window. This gives +you more control over the event flow and user experience. + +:::tip[Example of event handling] + +
Example + +```go +package main + +import ( + _ "embed" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +//go:embed assets +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Events Demo", + Description: "A demo of the Events API", + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Custom event handling + app.Events.On("myevent", func(e *application.WailsEvent) { + log.Printf("[Go] WailsEvent received: %+v\n", e) + }) + + // OS specific application events + app.Events.On(events.Mac.ApplicationDidFinishLaunching, func(event *application.Event) { + println("events.Mac.ApplicationDidFinishLaunching fired!") + }) + + // Platform agnostic events + app.Events.On(events.Common.ApplicationStarted, func(event *application.Event) { + println("events.Common.ApplicationStarted fired!") + }) + + win1 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Takes 3 attempts to close me!", + }) + + var countdown = 3 + + // Register a hook to cancel the window closing + win1.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + countdown-- + if countdown == 0 { + println("Closing!") + return + } + println("Nope! Not closing!") + e.Cancel() + }) + + win1.On(events.Common.WindowFocus, func(e *application.WindowEvent) { + println("[Event] Window focus!") + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} +``` + +
+ +::: + +## Wails Markup Language (wml) + +An experimental feature to call runtime methods using plain html, similar to +[htmx](https://htmx.org). + +:::tip[Example of wml] + +
Example + +```html + + + + + Wails ML Demo + + +

Wails ML Demo

+

This application contains no Javascript!

+ + + + + + + + + + +
+ Hover over me +
+ + +``` + +
+ +::: + +## Examples + +There are more examples available in the +[examples](https://github.com/wailsapp/wails/tree/v3-alpha/v3/examples) +directory. Check them out! diff --git a/docs/src/env.d.ts b/docs/src/env.d.ts new file mode 100644 index 000000000..acef35f17 --- /dev/null +++ b/docs/src/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/docs/src/stylesheets/extra.css b/docs/src/stylesheets/extra.css new file mode 100644 index 000000000..47e0c5386 --- /dev/null +++ b/docs/src/stylesheets/extra.css @@ -0,0 +1,6 @@ +@import './mermaid.css'; + +html { + scrollbar-gutter: stable; + overflow-y: scroll; /* Show vertical scrollbar */ +} diff --git a/docs/src/stylesheets/mermaid.css b/docs/src/stylesheets/mermaid.css new file mode 100644 index 000000000..4818131fa --- /dev/null +++ b/docs/src/stylesheets/mermaid.css @@ -0,0 +1,108 @@ +/* Mermaid diagram styling for Starlight */ + +/* Container for the whole diagram component */ +figure.expandable-diagram { + margin: 2rem 0; + padding: 1rem; + border-radius: 0.5rem; + background-color: var(--sl-color-gray-6); + box-shadow: var(--sl-shadow-sm); +} + +/* Dark mode adjustments */ +:root[data-theme="dark"] figure.expandable-diagram { + background-color: var(--sl-color-gray-1); +} + +/* Title for the diagram */ +figure.expandable-diagram figcaption { + font-weight: bold; + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--sl-color-text); +} + +/* Container for the actual diagram */ +.diagram-content { + display: flex; + justify-content: center; + margin: 1rem 0; + min-height: 100px; + overflow-x: auto; +} + +/* The diagram itself */ +.mermaid { + background-color: var(--sl-color-white); + padding: 1rem; + border-radius: 0.375rem; + max-width: 100%; +} + +:root[data-theme="dark"] .mermaid { + background-color: var(--sl-color-black); +} + +/* Source code details element */ +figure.expandable-diagram details { + margin-top: 1rem; + border-top: 1px solid var(--sl-color-gray-5); + padding-top: 0.5rem; +} + +:root[data-theme="dark"] figure.expandable-diagram details { + border-top-color: var(--sl-color-gray-3); +} + +/* Source button */ +figure.expandable-diagram summary { + cursor: pointer; + color: var(--sl-color-text-accent); + font-weight: 500; + display: inline-block; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; +} + +figure.expandable-diagram summary:hover { + background-color: var(--sl-color-gray-5); +} + +:root[data-theme="dark"] figure.expandable-diagram summary:hover { + background-color: var(--sl-color-gray-2); +} + +/* Source code */ +figure.expandable-diagram details pre { + margin-top: 0.5rem; + padding: 0.75rem; + border-radius: 0.375rem; + background-color: var(--sl-color-gray-7); + overflow-x: auto; +} + +:root[data-theme="dark"] figure.expandable-diagram details pre { + background-color: var(--sl-color-gray-0); +} + +/* Mermaid diagram specific adjustments */ +.mermaid .label { + font-family: var(--sl-font); + font-size: 0.9rem; +} + +/* Fix for diagram text in dark mode */ +:root[data-theme="dark"] .mermaid text { + fill: var(--sl-color-white); +} + +/* Ensure diagrams are responsive */ +@media (max-width: 768px) { + .diagram-content { + overflow-x: auto; + } + + .mermaid { + min-width: 100%; + } +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 000000000..bcbf8b509 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} diff --git a/mkdocs-website/docs/en/changelog.md b/mkdocs-website/docs/en/changelog.md new file mode 100644 index 000000000..0023f38ca --- /dev/null +++ b/mkdocs-website/docs/en/changelog.md @@ -0,0 +1,89 @@ +# Changelog + + + +## [Unreleased] + +### Added +- [darwin] add Event ApplicationShouldHandleReopen to be able to handle dock icon click by @5aaee9 in [#2991](https://github.com/wailsapp/wails/pull/2991) +- [darwin] add getPrimaryScreen/getScreens to impl by @tmclane in [#2618](https://github.com/wailsapp/wails/pull/2618) +- [darwin] add option for showing the toolbar in fullscreen mode on macOS by [@fbbdev](https://github.com/fbbdev) in [#3282](https://github.com/wailsapp/wails/pull/3282) +- [linux] add onKeyPress logic to convert linux keypress into an accelerator @[Atterpac](https://github.com/Atterpac) in[#3022](https://github.com/wailsapp/wails/pull/3022]) +- [linux] add task `run:linux` by [@marcus-crane](https://github.com/marcus-crane) in [#3146](https://github.com/wailsapp/wails/pull/3146) +- Export `SetIcon` method by [@almas-x](https://github.com/almas-x) in [PR](https://github.com/wailsapp/wails/pull/3147) +- Improve `OnShutdown` by [@almas-x](https://github.com/almas-x) in [PR](https://github.com/wailsapp/wails/pull/3189) +- Restore `ToggleMaximise` method in `Window` interface by [@fbbdev](https://github.com/fbbdev) in [#3281](https://github.com/wailsapp/wails/pull/3281) +- Added more information to `Environment()`. By @leaanthony in [aba82cc](https://github.com/wailsapp/wails/commit/aba82cc52787c97fb99afa58b8b63a0004b7ff6c) based on [PR](https://github.com/wailsapp/wails/pull/2044) by @Mai-Lapyst +- Expose the `WebviewWindow.IsFocused` method on the `Window` interface by [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- Support multiple space-separated trigger events in the WML system by [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- Add ESM exports from the bundled JS runtime script by [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- Add binding generator flag for using the bundled JS runtime script instead of the npm package by [@fbbdev](https://github.com/fbbdev) in [#3334](https://github.com/wailsapp/wails/pull/3334) +- Implement `setIcon` on linux by [@abichinger](https://github.com/abichinger) in [#3354](https://github.com/wailsapp/wails/pull/3354) +- Add flag `-port` to dev command and support environment variable `WAILS_VITE_PORT` by [@abichinger](https://github.com/abichinger) in [#3429](https://github.com/wailsapp/wails/pull/3429) +- Add tests for bound method calls by [@abichinger](https://github.com/abichinger) in [#3431](https://github.com/wailsapp/wails/pull/3431) + +### Fixed + +- [linux] Fixed theme handling error on NixOS by [tmclane](https://github.com/tmclane) in [#3515)(https://github.com/wailsapp/wails/pull/3515) +- Fixed cross volume project install for windows by [atterpac](https://github.com/atterac) in [#3512](https://github.com/wailsapp/wails/pull/3512) +- Fixed react template css to show footer by [atterpac](https://github.com/atterpac) in [#3477](https://github.com/wailsapp/wails/pull/3477) +- Fixed zombie processes when working in devmode by updating to latest refresh by [Atterpac](https://github.com/atterpac) in [#3320](https://github.com/wailsapp/wails/pull/3320). +- Fixed appimage webkit file sourcing by [Atterpac](https://github.com/atterpac) in [#3306](https://github.com/wailsapp/wails/pull/3306). +- Fixed Doctor apt package verify by [Atterpac](https://github.com/Atterpac) in [#2972](https://github.com/wailsapp/wails/pull/2972). +- Fixed application frozen when quit (Darwin) by @5aaee9 in [#2982](https://github.com/wailsapp/wails/pull/2982) +- Fixed background colours of examples on Windows by [mmgvh](https://github.com/mmghv) in [#2750](https://github.com/wailsapp/wails/pull/2750). +- Fixed default context menus by [mmgvh](https://github.com/mmghv) in [#2753](https://github.com/wailsapp/wails/pull/2753). +- Fixed hex values for arrow keys on Darwin by [jaybeecave](https://github.com/jaybeecave) in [#3052](https://github.com/wailsapp/wails/pull/3052). +- Set drag-n-drop for windows to working. Added by [@pylotlight](https://github.com/pylotlight) in [PR](https://github.com/wailsapp/wails/pull/3039) +- Fixed bug for linux in doctor in the event user doesn't have proper drivers installed. Added by [@pylotlight](https://github.com/pylotlight) in [PR](https://github.com/wailsapp/wails/pull/3032) +- Fix dpi scaling on start up (windows). Changed by [@almas-x](https://github.com/almas-x) in [PR](https://github.com/wailsapp/wails/pull/3145) +- Fix replace line in `go.mod` to use relative paths. Fixes Windows paths with spaces - @leaanthony. +- Fix MacOS systray click handling when no attached window by [thomas-senechal](https://github.com/thomas-senechal) in PR [#3207](https://github.com/wailsapp/wails/pull/3207) +- Fix failing Windows build due to unknown option by [thomas-senechal](https://github.com/thomas-senechal) in PR [#3208](https://github.com/wailsapp/wails/pull/3208) +- Fix crash on windows left clicking the systray icon when not having an attached window [tw1nk](https://github.com/tw1nk) in PR [#3271](https://github.com/wailsapp/wails/pull/3271) +- Fix wrong baseURL when open window twice by @5aaee9 in PR [#3273](https://github.com/wailsapp/wails/pull/3273) +- Fix ordering of if branches in `WebviewWindow.Restore` method by [@fbbdev](https://github.com/fbbdev) in [#3279](https://github.com/wailsapp/wails/pull/3279) +- Correctly compute `startURL` across multiple `GetStartURL` invocations when `FRONTEND_DEVSERVER_URL` is present. [#3299](https://github.com/wailsapp/wails/pull/3299) +- Fix the JS type of the `Screen` struct to match its Go counterpart by [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- Fix the `WML.Reload` method to ensure proper cleanup of registered event listeners by [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- Fix custom context menu closing immediately on linux by [@abichinger](https://github.com/abichinger) in [#3330](https://github.com/wailsapp/wails/pull/3330) +- Fix the output path and extension of model files produced by the binding generator by [@fbbdev](https://github.com/fbbdev) in [#3334](https://github.com/wailsapp/wails/pull/3334) +- Fix the import paths of model files in JS code produced by the binding generator by [@fbbdev](https://github.com/fbbdev) in [#3334](https://github.com/wailsapp/wails/pull/3334) +- Fix drag-n-drop on some linux distros by [@abichinger](https://github.com/abichinger) in [#3346](https://github.com/wailsapp/wails/pull/3346) +- Fix missing task for macOS when using `wails3 task dev` by [@hfoxy](https://github.com/hfoxy) in [#3417](https://github.com/wailsapp/wails/pull/3417) +- Fix registering events causing a nil map assignment by [@hfoxy](https://github.com/hfoxy) in [#3426](https://github.com/wailsapp/wails/pull/3426) +- Fix unmarshaling of bound method parameters by [@fbbdev](https://github.com/fbbdev) in [#3431](https://github.com/wailsapp/wails/pull/3431) +- Fix handling of multiple return values from bound methods by [@fbbdev](https://github.com/fbbdev) in [#3431](https://github.com/wailsapp/wails/pull/3431) +- Fix doctor detection of npm that is not installed with system package manager by [@pekim](https://github.com/pekim) in [#3458](https://github.com/wailsapp/wails/pull/3458) + +### Changed + +- Update linux webkit dependency to webkit2gtk-4.1 over webkitgtk2-4.0 to support Ubuntu 24.04 LTS by [atterpac](https://github.com/atterpac) in [#3461](https://github.com/wailsapp/wails/pull/3461) +- The bundled JS runtime script is now an ESM module: script tags importing it must have the `type="module"` attribute. By [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- The `@wailsio/runtime` package does not publish its API on the `window.wails` object, and does not start the WML system. This has been done to improve encapsulation. The WML system can be started manually if desired by calling the new `WML.Enable` method. The bundled JS runtime script still performs both operations automatically. By [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- The Window API module `@wailsio/runtime/src/window` now exposes the containing window object as a default export. It is not possible anymore to import individual methods through ESM named or namespace import syntax. +- The JS window API has been updated to match the current Go `WebviewWindow` API. Some methods have changed name or prototype, specifically: `Screen` becomes `GetScreen`; `GetZoomLevel`/`SetZoomLevel` become `GetZoom`/`SetZoom`; `GetZoom`, `Width` and `Height` now return values directly instead of wrapping them within objects. By [@fbbdev](https://github.com/fbbdev) in [#3295](https://github.com/wailsapp/wails/pull/3295) +- The binding generator now uses calls by ID by default. The `-id` CLI option has been removed. Use the `-names` CLI option to switch back to calls by name. By [@fbbdev](https://github.com/fbbdev) in [#3468](https://github.com/wailsapp/wails/pull/3468) +- New binding code layout: output files were previously organised in folders named after their containing package; now full Go import paths are used, including the module path. By [@fbbdev](https://github.com/fbbdev) in [#3468](https://github.com/wailsapp/wails/pull/3468) +- The struct field `application.Options.Bind` has been renamed to `application.Options.Services`. By [@fbbdev](https://github.com/fbbdev) in [#3468](https://github.com/wailsapp/wails/pull/3468) +- New syntax for binding services: service instances must now be wrapped in a call to `application.NewService`. By [@fbbdev](https://github.com/fbbdev) in [#3468](https://github.com/wailsapp/wails/pull/3468) +- Modified the `contentTypeSniffer` struct to include the `http.CloseNotifier` interface. Now compatible with Gin framework. By [@AnalogJ](https://github.com/AnalogJ) in [#3537](https://github.com/wailsapp/wails/pull/3537) + +### Removed + +### Deprecated + +### Security diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 000000000..215d80806 --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,29 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-go:latest diff --git a/scripts/AUTOMATION-README.md b/scripts/AUTOMATION-README.md new file mode 100644 index 000000000..4096b1781 --- /dev/null +++ b/scripts/AUTOMATION-README.md @@ -0,0 +1,123 @@ +# Wails Issue Management Automation + +This directory contains automation workflows and scripts to help manage the Wails project with minimal time investment. + +## GitHub Workflow Files + +### 1. Auto-Label Issues (`auto-label-issues.yml`) +- Automatically labels issues and PRs based on their content and modified files +- Labels are defined in `issue-labeler.yml` and `file-labeler.yml` +- Activates when issues are opened, edited, or reopened + +### 2. Issue Triage Automation (`issue-triage-automation.yml`) +- Performs automated actions for issue triage +- Requests more info for incomplete bug reports +- Prioritizes security issues +- Adds issues to appropriate project boards + +## Configuration Files + +### 1. Issue Content Labeler (`issue-labeler.yml`) +- Defines patterns to match in issue title/body +- Categorizes by version (v2/v3), component, type, and priority +- Customize patterns as needed for your project + +### 2. File Path Labeler (`file-labeler.yml`) +- Labels PRs based on which files they modify +- Helps identify which areas of the codebase are affected +- Customize file patterns as needed + +### 3. Stale Issues Config (`stale.yml`) +- Marks issues as stale after 45 days of inactivity +- Closes stale issues after an additional 10 days +- Exempts issues with important labels + +## Helper Scripts + +### 1. Issue Triage Script (`scripts/issue-triage.ps1`) +- PowerShell script to quickly triage issues +- Lists recent issues needing attention +- Provides easy keyboard shortcuts for common actions +- Run during your dedicated issue triage time + +### 2. PR Review Helper (`scripts/pr-review-helper.ps1`) +- PowerShell script to efficiently review PRs +- Generates review checklists +- Provides easy shortcuts for common review actions +- Run during your dedicated PR review time + +## How to Use This System + +### Daily Workflow (2 hours max) + +**Monday (120 min):** +1. Run `scripts/issue-triage.ps1` (30 min) +2. Run `scripts/pr-review-helper.ps1` (30 min) +3. Check Discord for critical discussions (30 min) +4. Plan your week (30 min) + +**Tuesday-Wednesday (120 min/day):** +1. Quick check for urgent issues (10 min) +2. v3 development (110 min) + +**Thursday (120 min):** +1. v2 maintenance (90 min) +2. Documentation updates (30 min) + +**Friday (120 min):** +1. Run `scripts/pr-review-helper.ps1` (60 min) +2. Discord updates/newsletter (30 min) +3. Weekly reflection (30 min) + +## Installation + +1. The GitHub workflow files should be placed in `.github/workflows/` +2. Configuration files should be placed in `.github/` +3. Helper scripts should be placed in `scripts/` +4. Make sure you have GitHub CLI (`gh`) installed and authenticated + +## Customization + +Feel free to modify the configuration files and scripts to better suit your project's needs: + +1. **Adding New Label Categories**: + - Add new patterns to `issue-labeler.yml` for additional components or types + - Update `file-labeler.yml` if you add new directories or file types + +2. **Adjusting Automation Thresholds**: + - Modify `stale.yml` to change how long issues remain active + - Update `issue-triage-automation.yml` to change conditions for automated actions + +3. **Customizing Scripts**: + - Update the scripts with your specific GitHub username + - Add additional actions based on your workflow preferences + - Adjust time allocations based on which tasks need more attention + +## Benefits + +This automated issue management system will: + +1. **Save Time**: Reduce manual triage of most common issues +2. **Improve Consistency**: Apply the same categorization rules every time +3. **Increase Visibility**: Clear categorization helps community members find issues +4. **Focus Development**: Clearer separation of v2 and v3 work +5. **Reduce Backlog**: Better management of stale issues +6. **Streamline Reviews**: Faster PR processing with guided workflows + +## Requirements + +- GitHub CLI (`gh`) installed and authenticated +- PowerShell 5.1+ for Windows scripts +- GitHub Actions enabled on your repository +- Appropriate permissions to modify workflows + +## Maintenance + +This system requires minimal maintenance: + +- Periodically review and update label patterns as your project evolves +- Adjust time allocations based on where you need to focus +- Update scripts if GitHub CLI commands change +- Customize the workflow as you find pain points in your process + +Remember that the goal is to maximize your limited time (2 hours per day) by automating repetitive tasks and streamlining essential ones. diff --git a/scripts/issue-triage.ps1 b/scripts/issue-triage.ps1 new file mode 100644 index 000000000..6f6edd3ad --- /dev/null +++ b/scripts/issue-triage.ps1 @@ -0,0 +1,108 @@ +# issue-triage.ps1 - Script to help with quick issue triage +# Run this at the start of your GitHub time to quickly process issues + +# Set your GitHub username +$GITHUB_USERNAME = "your-username" + +# Get the latest 10 open issues that aren't assigned and aren't labeled as "awaiting feedback" +Write-Host "Fetching recent unprocessed issues..." +gh issue list --repo wailsapp/wails --limit 10 --json number,title,labels,assignees | Out-File -Encoding utf8 -FilePath "issues_temp.json" +$issues = Get-Content -Raw -Path "issues_temp.json" | ConvertFrom-Json +$newIssues = $issues | Where-Object { + $_.assignees.Count -eq 0 -and + ($_.labels.Count -eq 0 -or -not ($_.labels | Where-Object { $_.name -eq "awaiting feedback" })) +} + +# Process each issue +Write-Host "`n===== Issues Needing Triage =====`n" +foreach ($issue in $newIssues) { + $number = $issue.number + $title = $issue.title + $labelNames = $issue.labels | ForEach-Object { $_.name } + $labelsStr = if ($labelNames) { $labelNames -join ", " } else { "none" } + + Write-Host "Issue #$number`: $title" + Write-Host "Labels: $labelsStr`n" + + $continue = $true + while ($continue) { + Write-Host "Options:" + Write-Host " [v] View issue in browser" + Write-Host " [2] Add v2-only label" + Write-Host " [3] Add v3-alpha label" + Write-Host " [b] Add bug label" + Write-Host " [e] Add enhancement label" + Write-Host " [d] Add documentation label" + Write-Host " [w] Add webview2 label" + Write-Host " [f] Request more info (awaiting feedback)" + Write-Host " [c] Close issue (duplicate/invalid)" + Write-Host " [a] Assign to yourself" + Write-Host " [s] Skip to next issue" + Write-Host " [q] Quit script" + $action = Read-Host "Enter action" + + switch ($action) { + "v" { + gh issue view $number --repo wailsapp/wails --web + } + "2" { + Write-Host "Adding v2-only label..." + gh issue edit $number --repo wailsapp/wails --add-label "v2-only" + } + "3" { + Write-Host "Adding v3-alpha label..." + gh issue edit $number --repo wailsapp/wails --add-label "v3-alpha" + } + "b" { + Write-Host "Adding bug label..." + gh issue edit $number --repo wailsapp/wails --add-label "Bug" + } + "e" { + Write-Host "Adding enhancement label..." + gh issue edit $number --repo wailsapp/wails --add-label "Enhancement" + } + "d" { + Write-Host "Adding documentation label..." + gh issue edit $number --repo wailsapp/wails --add-label "Documentation" + } + "w" { + Write-Host "Adding webview2 label..." + gh issue edit $number --repo wailsapp/wails --add-label "webview2" + } + "f" { + Write-Host "Requesting more info..." + gh issue comment $number --repo wailsapp/wails --body "Thank you for reporting this issue. Could you please provide additional information to help us investigate?`n`n- [Specific details needed]`n`nThis will help us address your issue more effectively." + gh issue edit $number --repo wailsapp/wails --add-label "awaiting feedback" + } + "c" { + $reason = Read-Host "Reason for closing (duplicate/invalid/etc)" + gh issue comment $number --repo wailsapp/wails --body "Closing this issue: $reason" + gh issue close $number --repo wailsapp/wails + } + "a" { + Write-Host "Assigning to yourself..." + gh issue edit $number --repo wailsapp/wails --add-assignee "$GITHUB_USERNAME" + } + "s" { + Write-Host "Skipping to next issue..." + $continue = $false + } + "q" { + Write-Host "Exiting script." + exit + } + default { + Write-Host "Invalid option. Please try again." + } + } + + Write-Host "" + } + + Write-Host "--------------------------------`n" +} + +Write-Host "No more issues to triage!" + +# Clean up temp file +Remove-Item -Path "issues_temp.json" diff --git a/scripts/issue-triage.sh b/scripts/issue-triage.sh new file mode 100644 index 000000000..5809b43a1 --- /dev/null +++ b/scripts/issue-triage.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# issue-triage.sh - Script to help with quick issue triage +# Run this at the start of your GitHub time to quickly process issues + +# Set your GitHub username +GITHUB_USERNAME="your-username" + +# Get the latest 10 open issues that aren't assigned and aren't labeled as "awaiting feedback" +echo "Fetching recent unprocessed issues..." +gh issue list --repo wailsapp/wails --limit 10 --json number,title,labels,assignees --jq '.[] | select(.assignees | length == 0) | select(any(.labels[]; .name != "awaiting feedback"))' > new_issues.json + +# Process each issue +echo -e "\n===== Issues Needing Triage =====\n" +cat new_issues.json | jq -c '.[]' | while read -r issue; do + number=$(echo $issue | jq -r '.number') + title=$(echo $issue | jq -r '.title') + labels=$(echo $issue | jq -r '.labels[] | .name' 2>/dev/null | tr '\n' ', ' | sed 's/,$//') + + if [ -z "$labels" ]; then + labels="none" + fi + + echo -e "Issue #$number: $title" + echo -e "Labels: $labels\n" + + while true; do + echo "Options:" + echo " [v] View issue in browser" + echo " [2] Add v2-only label" + echo " [3] Add v3-alpha label" + echo " [b] Add bug label" + echo " [e] Add enhancement label" + echo " [d] Add documentation label" + echo " [w] Add webview2 label" + echo " [f] Request more info (awaiting feedback)" + echo " [c] Close issue (duplicate/invalid)" + echo " [a] Assign to yourself" + echo " [s] Skip to next issue" + echo " [q] Quit script" + read -p "Enter action: " action + + case $action in + v) + gh issue view $number --repo wailsapp/wails --web + ;; + 2) + echo "Adding v2-only label..." + gh issue edit $number --repo wailsapp/wails --add-label "v2-only" + ;; + 3) + echo "Adding v3-alpha label..." + gh issue edit $number --repo wailsapp/wails --add-label "v3-alpha" + ;; + b) + echo "Adding bug label..." + gh issue edit $number --repo wailsapp/wails --add-label "Bug" + ;; + e) + echo "Adding enhancement label..." + gh issue edit $number --repo wailsapp/wails --add-label "Enhancement" + ;; + d) + echo "Adding documentation label..." + gh issue edit $number --repo wailsapp/wails --add-label "Documentation" + ;; + w) + echo "Adding webview2 label..." + gh issue edit $number --repo wailsapp/wails --add-label "webview2" + ;; + f) + echo "Requesting more info..." + gh issue comment $number --repo wailsapp/wails --body "Thank you for reporting this issue. Could you please provide additional information to help us investigate?\n\n- [Specific details needed]\n\nThis will help us address your issue more effectively." + gh issue edit $number --repo wailsapp/wails --add-label "awaiting feedback" + ;; + c) + read -p "Reason for closing (duplicate/invalid/etc): " reason + gh issue comment $number --repo wailsapp/wails --body "Closing this issue: $reason" + gh issue close $number --repo wailsapp/wails + ;; + a) + echo "Assigning to yourself..." + gh issue edit $number --repo wailsapp/wails --add-assignee "$GITHUB_USERNAME" + ;; + s) + echo "Skipping to next issue..." + break + ;; + q) + echo "Exiting script." + exit 0 + ;; + *) + echo "Invalid option. Please try again." + ;; + esac + + echo "" + done + + echo -e "--------------------------------\n" +done + +echo "No more issues to triage!" diff --git a/scripts/pr-review-helper.ps1 b/scripts/pr-review-helper.ps1 new file mode 100644 index 000000000..75fae4c3b --- /dev/null +++ b/scripts/pr-review-helper.ps1 @@ -0,0 +1,152 @@ +# pr-review-helper.ps1 - Script to help with efficient PR reviews +# Run this during your PR review time + +# Set your GitHub username +$GITHUB_USERNAME = "your-username" + +# Get open PRs that are ready for review +Write-Host "Fetching PRs ready for review..." +gh pr list --repo wailsapp/wails --json number,title,author,labels,reviewDecision,additions,deletions,baseRefName,headRefName --limit 10 | Out-File -Encoding utf8 -FilePath "prs_temp.json" +$prs = Get-Content -Raw -Path "prs_temp.json" | ConvertFrom-Json + +# Process each PR +Write-Host "`n===== PRs Needing Review =====`n" +foreach ($pr in $prs) { + $number = $pr.number + $title = $pr.title + $author = $pr.author.login + $labels = if ($pr.labels) { $pr.labels | ForEach-Object { $_.name } | Join-String -Separator ", " } else { "none" } + $reviewState = if ($pr.reviewDecision) { $pr.reviewDecision } else { "PENDING" } + $baseRef = $pr.baseRefName + $headRef = $pr.headRefName + $changes = $pr.additions + $pr.deletions + + Write-Host "PR #$number`: $title" + Write-Host "Author: $author" + Write-Host "Labels: $labels" + Write-Host "Branch: $headRef -> $baseRef" + Write-Host "Changes: +$($pr.additions)/-$($pr.deletions) lines" + Write-Host "Review state: $reviewState`n" + + # Determine complexity based on size + $complexity = if ($changes -lt 50) { + "Quick review" + } elseif ($changes -lt 300) { + "Moderate review" + } else { + "Extensive review" + } + + Write-Host "Complexity: $complexity" + + $continue = $true + while ($continue) { + Write-Host "`nOptions:" + Write-Host " [v] View PR in browser" + Write-Host " [d] View diff in browser" + Write-Host " [c] Generate review checklist" + Write-Host " [a] Approve PR" + Write-Host " [r] Request changes" + Write-Host " [m] Add comment" + Write-Host " [l] Add labels" + Write-Host " [s] Skip to next PR" + Write-Host " [q] Quit script" + $action = Read-Host "Enter action" + + switch ($action) { + "v" { + gh pr view $number --repo wailsapp/wails --web + } + "d" { + gh pr diff $number --repo wailsapp/wails --web + } + "c" { + # Generate review checklist + $checklist = @" +## PR Review: $title + +### Basic Checks: +- [ ] PR title is descriptive +- [ ] PR description explains the changes +- [ ] Related issues are linked + +### Technical Checks: +- [ ] Code follows project style +- [ ] No unnecessary commented code +- [ ] Error handling is appropriate +- [ ] Documentation updated (if needed) +- [ ] Tests included (if needed) + +### Impact Assessment: +- [ ] Changes are backward compatible (if applicable) +- [ ] No breaking changes to public APIs +- [ ] Performance impact considered + +### Version Specific: +"@ + + if ($baseRef -eq "master") { + $checklist += @" + +- [ ] Appropriate for v2 maintenance +- [ ] No features that should be v3-only +"@ + } elseif ($baseRef -eq "v3-alpha") { + $checklist += @" + +- [ ] Appropriate for v3 development +- [ ] Aligns with v3 roadmap +"@ + } + + # Write to clipboard + $checklist | Set-Clipboard + Write-Host "`nReview checklist copied to clipboard!`n" + } + "a" { + $comment = Read-Host "Approval comment (blank for none)" + if ($comment) { + gh pr review $number --repo wailsapp/wails --approve --body $comment + } else { + gh pr review $number --repo wailsapp/wails --approve + } + } + "r" { + $comment = Read-Host "Feedback for changes requested" + gh pr review $number --repo wailsapp/wails --request-changes --body $comment + } + "m" { + $comment = Read-Host "Comment text" + gh pr comment $number --repo wailsapp/wails --body $comment + } + "l" { + $labels = Read-Host "Labels to add (comma-separated)" + $labelArray = $labels -split "," + foreach ($label in $labelArray) { + $labelTrimmed = $label.Trim() + if ($labelTrimmed) { + gh pr edit $number --repo wailsapp/wails --add-label $labelTrimmed + } + } + } + "s" { + Write-Host "Skipping to next PR..." + $continue = $false + } + "q" { + Write-Host "Exiting script." + exit + } + default { + Write-Host "Invalid option. Please try again." + } + } + } + + Write-Host "--------------------------------`n" +} + +Write-Host "No more PRs to review!" + +# Clean up temp file +Remove-Item -Path "prs_temp.json" diff --git a/test-changelog-extraction.sh b/test-changelog-extraction.sh new file mode 100755 index 000000000..5fa8ae7f2 --- /dev/null +++ b/test-changelog-extraction.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Test script for changelog extraction logic +set -e + +echo "🧪 Testing Changelog Extraction Logic" +echo "======================================" + +# Test v2 changelog extraction +echo "📋 Testing v2 changelog extraction..." +CHANGELOG_FILE="website/src/pages/changelog.mdx" + +if [ ! -f "$CHANGELOG_FILE" ]; then + echo "❌ v2 changelog file not found: $CHANGELOG_FILE" + exit 1 +fi + +echo "✅ v2 changelog file found" + +# Extract unreleased section +awk ' +/^## \[Unreleased\]/ { found=1; next } +found && /^## / { exit } +found && !/^$/ { print } +' $CHANGELOG_FILE > test_v2_notes.md + +echo "📝 v2 extracted content:" +echo "------------------------" +if [ -s test_v2_notes.md ]; then + head -10 test_v2_notes.md + echo "..." + echo "✅ v2 changelog extraction successful ($(wc -l < test_v2_notes.md) lines)" +else + echo "⚠️ v2 unreleased section is empty" +fi + +echo "" + +# Test v3 changelog extraction (when on v3-alpha branch) +echo "📋 Testing v3 changelog extraction..." + +# Check if we can access v3 changelog +if git show v3-alpha:docs/src/content/docs/changelog.mdx > /dev/null 2>&1; then + echo "✅ v3 changelog accessible from v3-alpha branch" + + # Extract from v3-alpha branch + git show v3-alpha:docs/src/content/docs/changelog.mdx | awk ' + /^## \[Unreleased\]/ { found=1; next } + found && /^## / { exit } + found && !/^$/ { print } + ' > test_v3_notes.md + + echo "📝 v3 extracted content:" + echo "------------------------" + if [ -s test_v3_notes.md ]; then + head -10 test_v3_notes.md + echo "..." + echo "✅ v3 changelog extraction successful ($(wc -l < test_v3_notes.md) lines)" + else + echo "⚠️ v3 unreleased section is empty" + fi +else + echo "⚠️ v3 changelog not accessible (expected if not on v3-alpha branch)" +fi + +echo "" +echo "🧹 Cleaning up test files..." +rm -f test_v2_notes.md test_v3_notes.md + +echo "✅ Changelog extraction test completed!" \ No newline at end of file diff --git a/test-version-logic.sh b/test-version-logic.sh new file mode 100755 index 000000000..92abc50c3 --- /dev/null +++ b/test-version-logic.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Test script for version increment logic +set -e + +echo "🧪 Testing Version Increment Logic" +echo "==================================" + +# Test v2 version increment +echo "📈 Testing v2 version increment..." + +# Get current v2 version +CURRENT_V2=$(cat v2/cmd/wails/internal/version.txt | sed 's/^v//') +echo "Current v2 version: v$CURRENT_V2" + +# Parse version parts +IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_V2" +MAJOR=${VERSION_PARTS[0]} +MINOR=${VERSION_PARTS[1]} +PATCH=${VERSION_PARTS[2]} + +echo "Parsed: MAJOR=$MAJOR, MINOR=$MINOR, PATCH=$PATCH" + +# Test patch increment +PATCH_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" +echo "✅ Patch increment: v$CURRENT_V2 → v$PATCH_VERSION" + +# Test minor increment +MINOR_VERSION="$MAJOR.$((MINOR + 1)).0" +echo "✅ Minor increment: v$CURRENT_V2 → v$MINOR_VERSION" + +# Test major increment +MAJOR_VERSION="$((MAJOR + 1)).0.0" +echo "✅ Major increment: v$CURRENT_V2 → v$MAJOR_VERSION" + +echo "" + +# Test v3 version increment (simulate) +echo "📈 Testing v3 version increment..." + +# Simulate current v3 version +CURRENT_V3="v3.0.0-alpha.9" +echo "Simulated current v3 version: $CURRENT_V3" + +if [[ $CURRENT_V3 =~ v3\.0\.0-alpha\.([0-9]+) ]]; then + ALPHA_NUM=${BASH_REMATCH[1]} + NEW_ALPHA_NUM=$((ALPHA_NUM + 1)) + NEW_V3_VERSION="v3.0.0-alpha.$NEW_ALPHA_NUM" + echo "✅ Alpha increment: $CURRENT_V3 → $NEW_V3_VERSION" +else + echo "❌ Failed to parse v3 version format" + exit 1 +fi + +echo "" + +# Test conventional commit detection +echo "🔍 Testing Conventional Commit Detection..." + +# Simulate commit messages +COMMITS=" +feat: add new dialog API +fix: resolve memory leak +chore: update dependencies +feat!: remove deprecated API +docs: update README +BREAKING CHANGE: remove v1 compatibility +" + +echo "Test commits:" +echo "$COMMITS" + +# Test release type detection +if echo "$COMMITS" | grep -q "feat!\|fix!\|BREAKING CHANGE:"; then + RELEASE_TYPE="major" +elif echo "$COMMITS" | grep -q "feat\|BREAKING CHANGE"; then + RELEASE_TYPE="minor" +else + RELEASE_TYPE="patch" +fi + +echo "✅ Detected release type: $RELEASE_TYPE" + +echo "" +echo "✅ Version logic test completed!" \ No newline at end of file diff --git a/test-workflow.md b/test-workflow.md new file mode 100644 index 000000000..01421f9b1 --- /dev/null +++ b/test-workflow.md @@ -0,0 +1,61 @@ +# Testing the Nightly Release Workflow + +## Method 1: Fork Testing (Recommended) + +1. **Create a fork** of the Wails repository +2. **Push the workflow** to your fork +3. **Test manually** using `workflow_dispatch` +4. **Verify behavior** without affecting main repo + +```bash +# In your fork +git remote add upstream https://github.com/wailsapp/wails.git +git push origin master # Push workflow to your fork +``` + +## Method 2: Local Script Testing + +Create local test scripts to validate the logic: + +```bash +# Test changelog parsing +./test-changelog-extraction.sh + +# Test version increment logic +./test-version-logic.sh + +# Test commit analysis +./test-commit-detection.sh +``` + +## Method 3: Dry Run Workflow + +Add a `dry_run` input parameter to test without creating releases: + +```yaml +workflow_dispatch: + inputs: + dry_run: + description: 'Run in dry-run mode (no releases created)' + default: true + type: boolean +``` + +## Method 4: Act (GitHub Actions Local Runner) + +Use `act` to run GitHub Actions locally: + +```bash +brew install act +act workflow_dispatch -W .github/workflows/nightly-releases.yml +``` + +## Testing Checklist + +- [ ] Changelog parsing works correctly +- [ ] Version increment logic is accurate +- [ ] Conventional commit detection works +- [ ] Release notes format properly +- [ ] Authorization checks function +- [ ] Branch handling (master vs v3-alpha) +- [ ] Error handling and fallbacks \ No newline at end of file diff --git a/v2/cmd/wails/doctor.go b/v2/cmd/wails/doctor.go index 5306cab17..015ef8a0b 100644 --- a/v2/cmd/wails/doctor.go +++ b/v2/cmd/wails/doctor.go @@ -67,7 +67,7 @@ func diagnoseEnvironment(f *flags.Doctor) error { wailsTableData = append(wailsTableData, []string{"Package Manager", info.PM.Name()}) } - err = pterm.DefaultTable.WithData(wailsTableData).Render() + err = pterm.DefaultTable.WithBoxed().WithData(wailsTableData).Render() if err != nil { return err } diff --git a/v2/examples/customlayout/go.mod b/v2/examples/customlayout/go.mod index 005bb557d..ebd9bdbd0 100644 --- a/v2/examples/customlayout/go.mod +++ b/v2/examples/customlayout/go.mod @@ -29,11 +29,11 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wailsapp/go-webview2 v1.0.10 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect ) replace github.com/wailsapp/wails/v2 v2.1.0 => ../.. diff --git a/v2/examples/customlayout/go.sum b/v2/examples/customlayout/go.sum index 4ec20616f..6fd0e2a6b 100644 --- a/v2/examples/customlayout/go.sum +++ b/v2/examples/customlayout/go.sum @@ -63,11 +63,13 @@ github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhw github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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= @@ -80,10 +82,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/internal/app/app_devtools.go b/v2/internal/app/app_devtools.go index 60b221094..a84e8c283 100644 --- a/v2/internal/app/app_devtools.go +++ b/v2/internal/app/app_devtools.go @@ -1,8 +1,8 @@ -//go:build devtools - -package app - -// Note: devtools flag is also added in debug builds -func IsDevtoolsEnabled() bool { - return true -} +//go:build devtools + +package app + +// Note: devtools flag is also added in debug builds +func IsDevtoolsEnabled() bool { + return true +} diff --git a/v2/internal/frontend/desktop/darwin/WailsMenu.m b/v2/internal/frontend/desktop/darwin/WailsMenu.m index 66e5dd399..7e36da99a 100644 --- a/v2/internal/frontend/desktop/darwin/WailsMenu.m +++ b/v2/internal/frontend/desktop/darwin/WailsMenu.m @@ -184,16 +184,16 @@ return unicode(0x001b); } if( [key isEqualToString:@"left"] ) { - return unicode(0x001c); + return unicode(0xf702); } if( [key isEqualToString:@"right"] ) { - return unicode(0x001d); + return unicode(0xf703); } if( [key isEqualToString:@"up"] ) { - return unicode(0x001e); + return unicode(0xf700); } if( [key isEqualToString:@"down"] ) { - return unicode(0x001f); + return unicode(0xf701); } if( [key isEqualToString:@"space"] ) { return unicode(0x0020); diff --git a/v2/internal/frontend/desktop/windows/winc/w32/user32.go b/v2/internal/frontend/desktop/windows/winc/w32/user32.go index 8ca72ce4b..89ff985af 100644 --- a/v2/internal/frontend/desktop/windows/winc/w32/user32.go +++ b/v2/internal/frontend/desktop/windows/winc/w32/user32.go @@ -639,7 +639,7 @@ func GetSysColorBrush(nIndex int) HBRUSH { return HBRUSH(ret) */ - ret, _, _ := syscall.Syscall(getSysColorBrush, 1, + ret, _, _ := syscall.SyscallN(getSysColorBrush, uintptr(nIndex), 0, 0) diff --git a/v2/internal/go-common-file-dialog/cfd/iShellItem.go b/v2/internal/go-common-file-dialog/cfd/iShellItem.go index 6a747f4d9..c97efd8bb 100644 --- a/v2/internal/go-common-file-dialog/cfd/iShellItem.go +++ b/v2/internal/go-common-file-dialog/cfd/iShellItem.go @@ -40,8 +40,7 @@ func newIShellItem(path string) (*iShellItem, error) { func (vtbl *iShellItemVtbl) getDisplayName(objPtr unsafe.Pointer) (string, error) { var ptr *uint16 - ret, _, _ := syscall.Syscall(vtbl.GetDisplayName, - 2, + ret, _, _ := syscall.SyscallN(vtbl.GetDisplayName, uintptr(objPtr), 0x80058000, // SIGDN_FILESYSPATH uintptr(unsafe.Pointer(&ptr))) diff --git a/v2/internal/staticanalysis/test/standard/go.mod b/v2/internal/staticanalysis/test/standard/go.mod index 80e64f9cf..ae0c84abe 100644 --- a/v2/internal/staticanalysis/test/standard/go.mod +++ b/v2/internal/staticanalysis/test/standard/go.mod @@ -1,8 +1,8 @@ module changeme -go 1.18 +go 1.22 -require github.com/wailsapp/wails/v2 v2.3.1 +require github.com/wailsapp/wails/v2 v2.8.0 require ( github.com/bep/debounce v1.2.1 // indirect @@ -10,25 +10,25 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect - github.com/labstack/echo/v4 v4.9.1 // indirect + github.com/labstack/echo/v4 v4.10.2 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/leaanthony/go-ansi-parser v1.6.0 // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.6.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.4.2 // indirect - github.com/samber/lo v1.27.1 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/samber/lo v1.38.1 // indirect github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.1 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/v2/internal/staticanalysis/test/standard/go.sum b/v2/internal/staticanalysis/test/standard/go.sum index 0517c2888..96e20126c 100644 --- a/v2/internal/staticanalysis/test/standard/go.sum +++ b/v2/internal/staticanalysis/test/standard/go.sum @@ -13,6 +13,7 @@ github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4P github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y= github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= @@ -32,6 +33,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -41,8 +43,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/samber/lo v1.27.1 h1:sTXwkRiIFIQG+G0HeAvOEnGjqWeWtI9cg5/n51KrxPg= github.com/samber/lo v1.27.1/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= @@ -53,17 +57,22 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 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.3.1 h1:ZJz+pyIBKyASkgO8JO31NuHO1gTTHmvwiHYHwei1CqM= github.com/wailsapp/wails/v2 v2.3.1/go.mod h1:zlNLI0E2c2qA6miiuAHtp0Bac8FaGH0tlhA19OssR/8= +github.com/wailsapp/wails/v2 v2.8.0/go.mod h1:EFUGWkUX3KofO4fmKR/GmsLy3HhPH7NbyOEaMt8lBF0= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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= @@ -73,8 +82,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= diff --git a/v2/pkg/assetserver/assethandler.go b/v2/pkg/assetserver/assethandler.go index b8e2df076..b56a5d033 100644 --- a/v2/pkg/assetserver/assethandler.go +++ b/v2/pkg/assetserver/assethandler.go @@ -21,9 +21,6 @@ type Logger interface { Error(message string, args ...interface{}) } -//go:embed defaultindex.html -var defaultHTML []byte - const ( indexHTML = "index.html" ) @@ -120,7 +117,9 @@ func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, fi if err != nil { return err } - defer file.Close() + defer func() { + _ = file.Close() + }() statInfo, err := file.Stat() if err != nil { @@ -143,7 +142,9 @@ func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, fi if err != nil { return err } - defer file.Close() + defer func() { + _ = file.Close() + }() statInfo, err = file.Stat() if err != nil { diff --git a/v2/pkg/options/linux/linux.go b/v2/pkg/options/linux/linux.go index 797450c27..1287f1da2 100644 --- a/v2/pkg/options/linux/linux.go +++ b/v2/pkg/options/linux/linux.go @@ -4,10 +4,10 @@ package linux type WebviewGpuPolicy int const ( - // WebviewGpuPolicyAlways Hardware acceleration is always enabled. - WebviewGpuPolicyAlways WebviewGpuPolicy = iota // WebviewGpuPolicyOnDemand Hardware acceleration is enabled/disabled as request by web contents. - WebviewGpuPolicyOnDemand + WebviewGpuPolicyOnDemand WebviewGpuPolicy = iota + // WebviewGpuPolicyAlways Hardware acceleration is always enabled. + WebviewGpuPolicyAlways // WebviewGpuPolicyNever Hardware acceleration is always disabled. WebviewGpuPolicyNever ) diff --git a/v3/.gitignore b/v3/.gitignore new file mode 100644 index 000000000..429ef3836 --- /dev/null +++ b/v3/.gitignore @@ -0,0 +1,11 @@ +examples/kitchensink/kitchensink +cmd/wails3/wails +/examples/systray-menu/systray +/examples/window/window +/examples/dialogs/dialogs +/examples/menu/menu +/examples/clipboard/clipboard +/examples/plain/plain +/cmd/wails3/ui/.task/ +!internal/commands/webview2/MicrosoftEdgeWebview2Setup.exe +internal/commands/appimage_testfiles/appimage_testfiles \ No newline at end of file diff --git a/v3/.prettierignore b/v3/.prettierignore new file mode 100644 index 000000000..94c6af38e --- /dev/null +++ b/v3/.prettierignore @@ -0,0 +1 @@ +website \ No newline at end of file diff --git a/v3/.prettierrc.yml b/v3/.prettierrc.yml new file mode 100644 index 000000000..685d8b6e7 --- /dev/null +++ b/v3/.prettierrc.yml @@ -0,0 +1,6 @@ +overrides: + - files: + - "**/*.md" + options: + printWidth: 80 + proseWrap: always diff --git a/v3/README.md b/v3/README.md new file mode 100644 index 000000000..2d0c36a0b --- /dev/null +++ b/v3/README.md @@ -0,0 +1,9 @@ +# v3 Alpha + +Thanks for wanting to help out with testing/developing Wails v3! This guide will help you get started. + +## Getting Started + +All the instructions for getting started are in the v3 documentation directory: `mkdocs-website`. +Please read the README.md file in that directory for more information. + diff --git a/v3/TESTING.md b/v3/TESTING.md new file mode 100644 index 000000000..2e1486dc5 --- /dev/null +++ b/v3/TESTING.md @@ -0,0 +1,452 @@ +# Cross-Platform Testing Guide for Wails v3 + +This document describes the comprehensive cross-platform testing system for Wails v3 examples, supporting Mac, Linux, and Windows compilation. + +## Overview + +The testing system ensures all Wails v3 examples build successfully across all supported platforms: +- **macOS (Darwin)** - Native compilation +- **Windows** - Cross-compilation from any platform +- **Linux** - Multi-architecture Docker compilation (ARM64 + x86_64) + +## Test Directory Structure + +The testing infrastructure is organized in a dedicated test directory: + +```bash +v3/ +├── test/ +│ └── docker/ +│ ├── Dockerfile.linux-arm64 # ARM64 native compilation +│ └── Dockerfile.linux-x86_64 # x86_64 native compilation +├── Taskfile.yaml # Build task definitions +└── TESTING.md # This documentation +``` + +**Benefits of the organized structure:** +- **Separation of Concerns**: Testing files are isolated from application code +- **Clear Organization**: All Docker-related files in one location +- **Easier Maintenance**: Centralized testing infrastructure +- **Better Git Management**: Clean separation for .gitignore patterns + +## Available Commands + +### 🚀 Complete Cross-Platform Testing +```bash +# Build all examples for ALL platforms (macOS + Windows + Linux) +task test:examples:all +``` +**Total: 129 builds** (43 examples × 3 platforms) + CLI code testing + +### All Examples (No DIR Parameter Needed) +```bash +# Current platform only (all 43 examples + CLI code) +task test:examples + +# All examples for specific Linux architectures +task test:examples:linux:docker # Auto-detect architecture +task test:examples:linux:docker:arm64 # ARM64 native +task test:examples:linux:docker:x86_64 # x86_64 native + +# CLI code testing only +task test:cli +``` + +### Single Example Builds (Requires DIR=example) +```bash +# macOS/Darwin single example +task test:example:darwin DIR=badge + +# Windows cross-compilation single example +task test:example:windows DIR=badge + +# Linux native builds (on Linux systems) +task test:example:linux DIR=badge + +# Linux Docker builds (multi-architecture) +task test:example:linux:docker DIR=badge # Auto-detect architecture +task test:example:linux:docker:arm64 DIR=badge # ARM64 native +task test:example:linux:docker:x86_64 DIR=badge # x86_64 native +``` + +## Build Artifacts + +All builds generate platform-specific binaries with clear naming: +- **macOS**: `testbuild-{example}-darwin` +- **Windows**: `testbuild-{example}-windows.exe` +- **Linux**: `testbuild-{example}-linux` +- **Linux ARM64**: `testbuild-{example}-linux-arm64` (Docker) +- **Linux x86_64**: `testbuild-{example}-linux-x86_64` (Docker) + +Example outputs: +```text +examples/badge/testbuild-badge-darwin +examples/badge/testbuild-badge-windows.exe +examples/badge/testbuild-badge-linux-arm64 +examples/badge/testbuild-badge-linux-x86_64 +``` + +## Validation Status + +### ✅ **Production Ready (v3.0.0-alpha)** +- **Total Examples**: 43 examples fully tested +- **macOS**: ✅ All examples compile successfully (100%) +- **Windows**: ✅ All examples cross-compile successfully (100%) +- **Linux**: ✅ Multi-architecture Docker compilation (ARM64 + x86_64) +- **Build System**: Comprehensive Taskfile.yaml integration +- **Git Integration**: Complete .gitignore patterns for build artifacts +- **Total Build Capacity**: 129 cross-platform builds per test cycle + +## Supported Examples + +The system builds all 43 Wails v3 examples: +- badge, badge-custom, binding, build +- cancel-async, cancel-chaining, clipboard, contextmenus +- dev, dialogs, dialogs-basic, drag-n-drop +- environment, events, events-bug, file-association +- frameless, gin-example, gin-routing, gin-service +- hide-window, html-dnd-api, ignore-mouse, keybindings +- menu, notifications, panic-handling, plain +- raw-message, screen, services, show-macos-toolbar +- single-instance, systray-basic, systray-custom, systray-menu +- video, window, window-api, window-call +- window-menu, wml + +**Recently Added (v3.0.0-alpha):** +- dev, events-bug, gin-example, gin-routing, gin-service +- html-dnd-api, notifications + +## Platform Requirements + +### macOS (Darwin) +- Go 1.23+ +- Xcode Command Line Tools +- No additional dependencies required + +**Environment Variables:** +```bash +CGO_LDFLAGS="-framework UniformTypeIdentifiers -mmacosx-version-min=10.13" +CGO_CFLAGS="-mmacosx-version-min=10.13" +``` + +### Windows (Cross-compilation) +- Go 1.23+ +- No additional dependencies for cross-compilation + +**Environment Variables:** +```bash +GOOS=windows +GOARCH=amd64 +``` + +### Linux (Docker) - ✅ Multi-Architecture Support +Uses Ubuntu 24.04 base image with full GTK development environment: + +**Current Status:** Complete multi-architecture Docker compilation system +- ✅ ARM64 native compilation (Ubuntu 24.04) +- ✅ x86_64 native compilation (Ubuntu 24.04) +- ✅ Automatic architecture detection +- ✅ All dependencies install correctly (GTK + WebKit) +- ✅ Go 1.24 environment configured for each architecture +- ✅ Native compilation eliminates cross-compilation CGO issues + +**Architecture Support:** +- **ARM64**: Native compilation using `Dockerfile.linux-arm64` +- **x86_64**: Native compilation using `Dockerfile.linux-x86_64` with `--platform=linux/amd64` +- **Auto-detect**: Taskfile automatically selects appropriate architecture + +**Core Dependencies:** +- `build-essential` - GCC compiler toolchain (architecture-specific) +- `pkg-config` - Package configuration tool +- `libgtk-3-dev` - GTK+ 3.x development files +- `libwebkit2gtk-4.1-dev` - WebKit2GTK development files +- `git` - Version control (for go mod operations) +- `ca-certificates` - HTTPS support + +**Docker Images:** +- `wails-v3-linux-arm64` - Ubuntu 24.04 ARM64 native compilation (built from `test/docker/Dockerfile.linux-arm64`) +- `wails-v3-linux-x86_64` - Ubuntu 24.04 x86_64 native compilation (built from `test/docker/Dockerfile.linux-x86_64`) +- `wails-v3-linux-fixed` - Legacy unified image (deprecated) + +## Docker Configuration + +### Multi-Architecture Build System + +#### ARM64 Native Build Environment (`test/docker/Dockerfile.linux-arm64`) +```dockerfile +FROM ubuntu:24.04 +# ARM64 native compilation environment +# Go 1.24.0 ARM64 binary (go1.24.0.linux-arm64.tar.gz) +# Native GCC toolchain for ARM64 +# All GTK/WebKit dependencies for ARM64 +# Build script: /build/build-linux-arm64.sh +# Output: testbuild-{example}-linux-arm64 +``` + +#### x86_64 Native Build Environment (`test/docker/Dockerfile.linux-x86_64`) +```dockerfile +FROM --platform=linux/amd64 ubuntu:24.04 +# x86_64 native compilation environment +# Go 1.24.0 x86_64 binary (go1.24.0.linux-amd64.tar.gz) +# Native GCC toolchain for x86_64 +# All GTK/WebKit dependencies for x86_64 +# Build script: /build/build-linux-x86_64.sh +# Output: testbuild-{example}-linux-x86_64 +``` + +### Available Docker Tasks + +#### Architecture-Specific Tasks +```bash +# ARM64 builds +task test:example:linux:docker:arm64 DIR=badge +task test:examples:linux:docker:arm64 + +# x86_64 builds +task test:example:linux:docker:x86_64 DIR=badge +task test:examples:linux:docker:x86_64 +``` + +#### Auto-Detection Tasks (Recommended) +```bash +# Single example (auto-detects host architecture) +task test:example:linux:docker DIR=badge + +# All examples (auto-detects host architecture) +task test:examples:linux:docker +``` + +## Implementation Details + +### Key Fixes Applied in v3.0.0-alpha + +#### 1. **Complete Example Coverage** +- **Before**: 35 examples tested +- **After**: 43 examples tested (100% coverage) +- **Added**: dev, events-bug, gin-example, gin-routing, gin-service, html-dnd-api, notifications + +#### 2. **Go Module Resolution** +- **Issue**: Inconsistent replace directives across examples +- **Fix**: Standardized all examples to use `replace github.com/wailsapp/wails/v3 => ../..` +- **Examples Fixed**: gin-example, gin-routing, notifications + +#### 3. **Frontend Asset Embedding** +- **Issue**: Some examples referenced missing `frontend/dist` directories +- **Fix**: Updated embed paths from `//go:embed all:frontend/dist` to `//go:embed all:frontend` +- **Examples Fixed**: file-association, notifications + +#### 4. **Manager API Migration** +- **Issue**: Windows badge service using deprecated API +- **Fix**: Updated `app.CurrentWindow()` → `app.Windows.Current()` +- **Files Fixed**: pkg/services/badge/badge_windows.go + +#### 5. **File Association Example** +- **Issue**: Undefined window variable +- **Fix**: Added proper window assignment from `app.Windows.NewWithOptions()` +- **Files Fixed**: examples/file-association/main.go + +### Build Performance +- **macOS**: ~2-3 minutes for all 43 examples +- **Windows Cross-Compile**: ~2-3 minutes for all 43 examples +- **Linux Docker**: ~5-10 minutes for all 43 examples (includes image build) +- **Total Build Time**: ~10-15 minutes for complete cross-platform validation (129 builds) + +## Usage Examples + +### Single Example Testing (Requires DIR Parameter) +```bash +# Test the badge example on all platforms +task test:example:darwin DIR=badge # macOS native +task test:example:windows DIR=badge # Windows cross-compile +task test:example:linux:docker DIR=badge # Linux Docker (auto-detect arch) +``` + +### All Examples Testing (No DIR Parameter) +```bash +# Test everything - all 43 examples, all platforms +task test:examples:all + +# This runs: +# 1. All Darwin builds (43 examples) +# 2. All Windows cross-compilation (43 examples) +# 3. All Linux Docker builds (43 examples, auto-architecture) + +# Platform-specific all examples +task test:examples # Current platform (43 examples) +task test:examples:linux:docker:arm64 # ARM64 builds (43 examples) +task test:examples:linux:docker:x86_64 # x86_64 builds (43 examples) +``` + +### Continuous Integration +```bash +# For CI/CD pipelines +task test:examples:all # Complete cross-platform (129 builds) +task test:examples # Current platform only (43 builds) +``` + +## Build Process Details + +### macOS Builds +1. Sets macOS-specific CGO flags for compatibility +2. Runs `go mod tidy` in each example directory +3. Compiles with `go build -o testbuild-{example}-darwin` +4. Links against UniformTypeIdentifiers framework + +### Windows Cross-Compilation +1. Sets `GOOS=windows GOARCH=amd64` environment +2. Runs `go mod tidy` in each example directory +3. Cross-compiles with `go build -o testbuild-{example}-windows.exe` +4. No CGO dependencies required (uses Windows APIs) + +### Linux Docker Builds +1. **Auto-Detection**: Detects host architecture (ARM64 or x86_64) +2. **Image Selection**: Uses appropriate Ubuntu 24.04 image for target architecture +3. **Native Compilation**: Eliminates cross-compilation CGO issues +4. **Environment Setup**: Full GTK/WebKit development environment +5. **Build Process**: Runs `go mod tidy && go build` with native toolchain +6. **Output**: Architecture-specific binaries (`-linux-arm64` or `-linux-x86_64`) + +## Troubleshooting + +### Common Issues (All Resolved in v3.0.0-alpha) + +#### **Go Module Resolution Errors** +```bash +Error: replacement directory ../wails/v3 does not exist +``` +**Solution**: All examples now use standardized `replace github.com/wailsapp/wails/v3 => ../..` + +#### **Frontend Asset Embedding Errors** +```bash +Error: pattern frontend/dist: no matching files found +``` +**Solution**: Updated to `//go:embed all:frontend` for examples without dist directories + +#### **Manager API Errors** +```bash +Error: app.CurrentWindow undefined +``` +**Solution**: Updated to use new manager pattern `app.Windows.Current()` + +#### **Build Warnings** +Some examples may show compatibility warnings (e.g., notifications using macOS 10.14+ APIs with 10.13 target). These are non-blocking warnings that can be addressed separately. + +### Performance Optimization + +#### **Parallel Builds** +```bash +# The task system automatically runs builds in parallel where possible +task v3:test:examples:all # Optimized for maximum throughput +``` + +#### **Selective Testing** +```bash +# Test specific examples to debug issues +task v3:test:example:darwin DIR=badge +task v3:test:example:windows DIR=contextmenus +``` + +### Performance Tips + +**Parallel Builds:** +```bash +# Build multiple examples simultaneously +task v3:test:example:darwin DIR=badge & +task v3:test:example:darwin DIR=binding & +task v3:test:example:darwin DIR=build & +wait +``` + +**Docker Image Caching:** +```bash +# Pre-build Docker images +docker build -f Dockerfile.linux -t wails-v3-linux-builder . +docker build -f Dockerfile.linux-simple -t wails-v3-linux-simple . +``` + +## Integration with Git + +### Ignored Files +All build artifacts are automatically ignored via `.gitignore`: +```gitignore +/v3/examples/*/testbuild-* +``` + +### Clean Build Environment +```bash +# Remove all test build artifacts +find v3/examples -name "testbuild-*" -delete +``` + +## Validation Results + +### Current Status (as of implementation): +- ✅ **macOS**: All 43 examples compile successfully +- ✅ **Windows**: All 43 examples cross-compile successfully +- ✅ **Linux**: Multi-architecture Docker system fully functional + +### Build Time Estimates: +- **macOS**: ~2-3 minutes for all examples +- **Windows**: ~2-3 minutes for all examples (cross-compile) +- **Linux Docker**: ~5-10 minutes for all examples (includes image build and compilation) +- **Complete Cross-Platform**: ~10-15 minutes for 129 total builds + +## Future Enhancements + +### Planned Improvements: +1. **Automated Testing**: Add runtime testing in addition to compilation +2. **Multi-Architecture**: Support ARM64 builds for Apple Silicon and Windows ARM +3. **Build Caching**: Implement Go build cache for faster repeated builds +4. **Parallel Docker**: Multi-stage Docker builds for faster Linux compilation +5. **Platform Matrix**: GitHub Actions integration for automated CI/CD + +### Platform Extensions: +- **FreeBSD**: Add BSD build support +- **Android/iOS**: Mobile platform compilation (when supported) +- **WebAssembly**: WASM target compilation + +## Changelog + +### v3.0.0-alpha (2025-06-20) +#### 🎯 Complete Cross-Platform Testing System + +#### **✨ New Features** +- **Complete Example Coverage**: All 43 examples now tested (was 35) +- **Cross-Platform Validation**: Mac + Windows builds for all examples +- **Standardized Build Artifacts**: Consistent platform-specific naming +- **Enhanced Git Integration**: Complete .gitignore patterns for build artifacts + +#### **🐛 Major Fixes** +- **Go Module Resolution**: Standardized replace directives across all examples +- **Frontend Asset Embedding**: Fixed missing frontend/dist directory references +- **Manager API Migration**: Updated deprecated Windows badge service calls +- **File Association**: Fixed undefined window variable +- **Build Completeness**: Added 8 missing examples to test suite + +#### **🔧 Infrastructure Improvements** +- **Taskfile Integration**: Comprehensive cross-platform build tasks +- **Performance Optimization**: Parallel builds where possible +- **Error Handling**: Clear build failure reporting and debugging +- **Documentation**: Complete testing guide with troubleshooting + +#### **📊 Validation Results** +- **macOS**: ✅ 43/43 examples compile successfully +- **Windows**: ✅ 43/43 examples cross-compile successfully +- **Build Time**: ~5-6 minutes for complete cross-platform validation +- **Reliability**: 100% success rate with proper error handling + +## Support + +For issues with cross-platform builds: +1. Check platform-specific requirements above +2. Review the troubleshooting section for resolved issues +3. Verify Go 1.24+ is installed +4. Check build logs for specific error messages +5. Use selective testing to isolate problems + +## References + +- [Wails v3 Documentation](https://wails.io/docs/) +- [Go Cross Compilation](https://golang.org/doc/install/cross) +- [GTK Development Libraries](https://www.gtk.org/docs/installations/linux) +- [Task Runner Documentation](https://taskfile.dev/) \ No newline at end of file diff --git a/v3/Taskfile.yaml b/v3/Taskfile.yaml new file mode 100644 index 000000000..04afd60f4 --- /dev/null +++ b/v3/Taskfile.yaml @@ -0,0 +1,371 @@ +# https://taskfile.dev + +version: "3" + +includes: + generator: + taskfile: ./internal/generator + dir: ./internal/generator + + runtime: + taskfile: ./internal/runtime + dir: ./internal/runtime + + website: + taskfile: ./website + dir: ./website + optional: true + + docs: + taskfile: ../docs + dir: ../docs + optional: true + +tasks: + recreate-template-dir: + dir: internal/templates + internal: true + silent: true + cmds: + - rm -rf {{.TEMPLATE_DIR}} + - mkdir -p {{.TEMPLATE_DIR}} + + install: + dir: cmd/wails3 + silent: true + cmds: + - go install + - echo "Installed wails CLI" + + release: + summary: Release a new version of Wails. Call with `task v3:release -- ` + dir: tasks/release + cmds: + - go run release.go {{.CLI_ARGS}} + + taskfile:upgrade: + cmds: + - go get -u github.com/go-task/task/v3 + + reinstall-cli: + dir: cmd/wails3 + internal: true + silent: true + cmds: + - go install + - echo "Reinstalled wails CLI" + + generate:events: + dir: tasks/events + cmds: + - go run generate.go + - go fmt ../../pkg/events/events.go + + precommit: + cmds: + - go test ./... + - task: format +# - task: docs:update:api + + test:example:darwin: + dir: 'examples/{{.DIR}}' + platforms: + - darwin + cmds: + - echo "Building example {{.DIR}} for Darwin" + - go mod tidy + - go build -o "testbuild-{{.DIR}}-darwin{{exeExt}}" + env: + CGO_LDFLAGS: -framework UniformTypeIdentifiers -mmacosx-version-min=10.13 + CGO_CFLAGS: -mmacosx-version-min=10.13 + + test:example:windows: + dir: 'examples/{{.DIR}}' + platforms: + - windows + cmds: + - echo "Building example {{.DIR}} for Windows" + - go mod tidy + - go build -o "testbuild-{{.DIR}}-windows.exe" + env: + GOOS: windows + GOARCH: amd64 + + test:example:linux: + dir: 'examples/{{.DIR}}' + platforms: + - linux + cmds: + - echo "Building example {{.DIR}} for Linux" + - go mod tidy + - go build -o "testbuild-{{.DIR}}-linux" + + test:example:linux:docker:arm64: + summary: Build a single example for Linux ARM64 using Docker (Ubuntu 24.04) + cmds: + - echo "Building example {{.DIR}} for Linux ARM64 using Docker" + - docker build --pull -f test/docker/Dockerfile.linux-arm64 -t wails-v3-linux-arm64 . + - docker run --rm wails-v3-linux-arm64 /build/build-linux-arm64.sh {{.DIR}} + + test:example:linux:docker:x86_64: + summary: Build a single example for Linux x86_64 using Docker (Ubuntu 24.04) + cmds: + - echo "Building example {{.DIR}} for Linux x86_64 using Docker" + - docker build --pull -f test/docker/Dockerfile.linux-x86_64 -t wails-v3-linux-x86_64 . + - docker run --rm wails-v3-linux-x86_64 /build/build-linux-x86_64.sh {{.DIR}} + + test:examples:linux:docker:arm64: + summary: Build all examples for Linux ARM64 using Docker (Ubuntu 24.04) + cmds: + - echo "Building Docker image for Linux ARM64 compilation..." + - docker build --pull -f test/docker/Dockerfile.linux-arm64 -t wails-v3-linux-arm64 . + - echo "Running Linux ARM64 compilation in Docker container..." + - docker run --rm wails-v3-linux-arm64 + + test:examples:linux:docker:x86_64: + summary: Build all examples for Linux x86_64 using Docker (Ubuntu 24.04) + cmds: + - echo "Building Docker image for Linux x86_64 compilation..." + - docker build --pull -f test/docker/Dockerfile.linux-x86_64 -t wails-v3-linux-x86_64 . + - echo "Running Linux x86_64 compilation in Docker container..." + - docker run --rm wails-v3-linux-x86_64 + + test:example:linux:docker: + summary: Build a single example for Linux using Docker (auto-detect architecture) + cmds: + - echo "Auto-detecting architecture for Linux Docker build..." + - | + if [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then + echo "Detected ARM64, using ARM64 Docker image" + task test:example:linux:docker:arm64 DIR={{.DIR}} + else + echo "Detected x86_64, using x86_64 Docker image" + task test:example:linux:docker:x86_64 DIR={{.DIR}} + fi + + test:examples:linux:docker: + summary: Build all examples for Linux using Docker (auto-detect architecture) + cmds: + - echo "Auto-detecting architecture for Linux Docker build..." + - | + if [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then + echo "Detected ARM64, using ARM64 Docker image" + task test:examples:linux:docker:arm64 + else + echo "Detected x86_64, using x86_64 Docker image" + task test:examples:linux:docker:x86_64 + fi + + test:examples:all: + summary: Builds all examples for all platforms (Mac + Windows + Linux via Docker) + vars: + EXAMPLEDIRS: | + badge + badge-custom + binding + build + cancel-async + cancel-chaining + clipboard + contextmenus + dev + dialogs + dialogs-basic + drag-n-drop + environment + events + events-bug + file-association + frameless + gin-example + gin-routing + gin-service + hide-window + html-dnd-api + ignore-mouse + keybindings + liquid-glass + menu + notifications + panic-handling + plain + raw-message + screen + services + show-macos-toolbar + single-instance + systray-basic + systray-custom + systray-menu + video + window + window-api + window-call + window-menu + wml + cmds: + - echo "Building all examples for all platforms..." + - echo "=== Building for Darwin ===" + - for: { var: EXAMPLEDIRS } + task: test:example:darwin + vars: + DIR: "{{.ITEM}}" + - echo "=== Building for Windows (cross-compile) ===" + - for: { var: EXAMPLEDIRS } + task: test:example:windows + vars: + DIR: "{{.ITEM}}" + - echo "=== Building for Linux (Docker) ===" + - task: test:examples:linux:docker + - echo "=== Testing CLI Code ===" + - task: test:cli + - echo "=== Cleaning Up Test Binaries ===" + - task: clean:test:binaries + + test:cli: + summary: Test CLI-related code compilation + cmds: + - echo "Testing CLI appimage testfiles compilation..." + - cd internal/commands/appimage_testfiles && go mod tidy && go build + - echo "✅ CLI appimage testfiles compile successfully" + + test:cli:all: + summary: Test all CLI components and critical test files + cmds: + - echo "Testing CLI appimage testfiles..." + - cd internal/commands/appimage_testfiles && go mod tidy && go build + - echo "Testing window visibility test..." + - cd tests/window-visibility-test && go mod tidy && go build + - echo "Testing service implementations..." + - cd pkg/services/badge && go build + - echo "✅ All CLI components compile successfully" + + test:generator: + summary: Test code generator test cases compilation + cmds: + - echo "Testing generator test cases (sample)..." + - cd internal/generator/testcases/function_single && go mod tidy && go build + - cd internal/generator/testcases/complex_method && go mod tidy && go build + - cd internal/generator/testcases/struct_literal_single && go mod tidy && go build + - echo "✅ Generator test cases compile successfully" + + test:templates: + summary: Test template generation for core templates + cmds: + - echo "Testing template generation (core templates)..." + - task: install + - echo "Testing lit template generation..." + - rm -rf ./test-template-lit && wails3 init -n test-template-lit -t lit + - mkdir -p ./test-template-lit/frontend/dist && touch ./test-template-lit/frontend/dist/.keep + - cd ./test-template-lit && go mod tidy && go build + - rm -rf ./test-template-lit + - echo "Testing react template generation..." + - rm -rf ./test-template-react && wails3 init -n test-template-react -t react + - mkdir -p ./test-template-react/frontend/dist && touch ./test-template-react/frontend/dist/.keep + - cd ./test-template-react && go mod tidy && go build + - rm -rf ./test-template-react + - echo "✅ Template generation tests completed successfully" + + test:infrastructure: + summary: Test critical infrastructure components + cmds: + - echo "=== Testing CLI Components ===" + - task: test:cli:all + - echo "=== Testing Generator ===" + - task: test:generator + - echo "=== Testing Templates ===" + - task: test:templates + - echo "=== Testing pkg/application ===" + - cd pkg/application && go test -c -o /dev/null ./... + - echo "=== Cleaning Up Test Binaries ===" + - task: clean:test:binaries + - echo "✅ All infrastructure components test successfully" + + test:examples: + summary: Builds the examples for current platform only + vars: + EXAMPLEDIRS: | + badge + badge-custom + binding + build + cancel-async + cancel-chaining + clipboard + contextmenus + dev + dialogs + dialogs-basic + drag-n-drop + environment + events + events-bug + file-association + frameless + gin-example + gin-routing + gin-service + hide-window + html-dnd-api + ignore-mouse + keybindings + liquid-glass + menu + notifications + panic-handling + plain + raw-message + screen + services + show-macos-toolbar + single-instance + systray-basic + systray-custom + systray-menu + video + window + window-api + window-call + window-menu + wml + cmds: + - echo "Testing examples compilation..." + - for: { var: EXAMPLEDIRS } + task: test:example:darwin + vars: + DIR: "{{.ITEM}}" + platforms: [darwin] + - for: { var: EXAMPLEDIRS } + task: test:example:linux + vars: + DIR: "{{.ITEM}}" + platforms: [linux] + - for: { var: EXAMPLEDIRS } + task: test:example:windows + vars: + DIR: "{{.ITEM}}" + platforms: [windows] + - echo "Testing CLI code..." + - task: test:cli + - echo "=== Cleaning Up Test Binaries ===" + - task: clean:test:binaries + + clean:test:binaries: + summary: Clean up all test-generated binary files and directories (cross-platform) + cmds: + - echo "🧹 Cleaning up test binaries..." + - go run tasks/cleanup/cleanup.go + - echo "✅ Test binaries cleaned up" + + test:all: + summary: Run all tests including examples, infrastructure, and Go unit tests + cmds: + - echo "=== Running Go Unit Tests ===" + - go test ./... + - echo "=== Testing Examples (Current Platform) ===" + - task: test:examples + - echo "=== Testing Infrastructure Components ===" + - task: test:infrastructure + - echo "=== Cleaning Up Test Binaries ===" + - task: clean:test:binaries + - echo "✅ All tests completed successfully" diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md new file mode 100644 index 000000000..ce00c034d --- /dev/null +++ b/v3/UNRELEASED_CHANGELOG.md @@ -0,0 +1,59 @@ +# Unreleased Changes + + + +## Added + + +## Changed + +- macOS: Use `visibleFrame` instead of `frame` for window centering to exclude menu bar and dock areas + +## Fixed + +- Fixed redefinition error for liquid glass demo in [#4542](https://github.com/wailsapp/wails/pull/4542) by @Etesam913 + +## Deprecated + + +## Removed + + +## Security + + +--- + +### Example Entries: + +**Added:** + +- Add support for custom window icons in application options +- Add new `SetWindowIcon()` method to runtime API (#1234) + +**Changed:** + +- Update minimum Go version requirement to 1.21 +- Improve error messages for invalid configuration files + +**Fixed:** + +- Fix memory leak in event system during window close operations (#5678) +- Fix crash when using context menus on Linux with Wayland + +**Security:** + +- Update dependencies to address CVE-2024-12345 in third-party library diff --git a/v3/cmd/wails3/README.md b/v3/cmd/wails3/README.md new file mode 100644 index 000000000..8924153dd --- /dev/null +++ b/v3/cmd/wails3/README.md @@ -0,0 +1,83 @@ +# The Wails CLI + +The Wails CLI is a command line tool that allows you to create, build and run Wails applications. +There are a number of commands related to tooling, such as icon generation and asset bundling. + +## Commands + +### task + +The `task` command is for running tasks defined in `Taskfile.yml`. It is a wrapper around [Task](https://taskfile.dev). + +### generate + +The `generate` command is used to generate resources and assets for your Wails project. +It can be used to generate many things including: + - application icons, + - resource files for Windows applications + - Info.plist files for macOS deployments + +#### icon + +The `icon` command generates icons for your project. + +| Flag | Type | Description | Default | +|--------------------|--------|------------------------------------------------------|----------------------| +| `-example` | bool | Generates example icon file (appicon.png) | | +| `-input` | string | The input image file | | +| `-sizes` | string | The sizes to generate in .ico file (comma separated) | "256,128,64,48,32,16" | +| `-windowsFilename` | string | The output filename for the Windows icon | icon.ico | +| `-macFilename` | string | The output filename for the Mac icon bundle | icons.icns | + +```bash +wails3 generate icon -input myicon.png -sizes "32,64,128" -windowsFilename myicon.ico -macFilename myicon.icns +``` + +This will generate icons for mac and windows and save them in the current directory as `myicon.ico` +and `myicons.icns`. + +#### syso + +The `syso` command generates a Windows resource file (aka `.syso`). + +```bash +wails3 generate syso +``` + +| Flag | Type | Description | Default | +|-------------|--------|--------------------------------------------|------------------| +| `-example` | bool | Generates example manifest & info files | | +| `-manifest` | string | The manifest file | | +| `-info` | string | The info.json file | | +| `-icon` | string | The icon file | | +| `-out` | string | The output filename for the syso file | `wails.exe.syso` | +| `-arch` | string | The target architecture (amd64,arm64,386) | `runtime.GOOS` | + +If `-example` is provided, the command will generate example manifest and info files +in the current directory and exit. + +If `-manifest` is provided, the command will use the provided manifest file to generate +the syso file. + +If `-info` is provided, the command will use the provided info.json file to set the version +information in the syso file. + +NOTE: We use [winres](https://github.com/tc-hib/winres) to generate the syso file. Please +refer to the winres documentation for more information. + +NOTE: Whilst the tool will work for 32-bit Windows, it is not supported. Please use 64-bit. + +#### defaults + +```bash +wails3 generate defaults +``` +This will generate all the default assets and resources in the current directory. + +#### bindings + +```bash +wails3 generate bindings +``` + +Generates bindings and models for your bound Go methods and structs. \ No newline at end of file diff --git a/v3/cmd/wails3/main.go b/v3/cmd/wails3/main.go new file mode 100644 index 000000000..6800134ed --- /dev/null +++ b/v3/cmd/wails3/main.go @@ -0,0 +1,136 @@ +package main + +import ( + "os" + "runtime/debug" + + "github.com/pkg/browser" + + "github.com/pterm/pterm" + "github.com/samber/lo" + + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v3/internal/commands" + "github.com/wailsapp/wails/v3/internal/flags" + "github.com/wailsapp/wails/v3/internal/term" +) + +func init() { + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + return + } + commands.BuildSettings = lo.Associate(buildInfo.Settings, func(setting debug.BuildSetting) (string, string) { + return setting.Key, setting.Value + }) + // Iterate over the Deps and add them to the build settings using a prefix of "mod." + for _, dep := range buildInfo.Deps { + commands.BuildSettings["mod."+dep.Path] = dep.Version + } +} + +func main() { + app := clir.NewCli("wails", "The Wails3 CLI", "v3") + app.NewSubCommand("docs", "Open the docs").Action(openDocs) + app.NewSubCommandFunction("init", "Initialise a new project", commands.Init) + + build := app.NewSubCommand("build", "Build the project") + var buildFlags flags.Build + build.AddFlags(&buildFlags) + build.Action(func() error { + return commands.Build(&buildFlags, build.OtherArgs()) + }) + + app.NewSubCommandFunction("dev", "Run in Dev mode", commands.Dev) + + pkg := app.NewSubCommand("package", "Package application") + var pkgFlags flags.Package + pkg.AddFlags(&pkgFlags) + pkg.Action(func() error { + return commands.Package(&pkgFlags, pkg.OtherArgs()) + }) + app.NewSubCommandFunction("doctor", "System status report", commands.Doctor) + app.NewSubCommandFunction("releasenotes", "Show release notes", commands.ReleaseNotes) + + task := app.NewSubCommand("task", "Run and list tasks") + var taskFlags commands.RunTaskOptions + task.AddFlags(&taskFlags) + task.Action(func() error { + return commands.RunTask(&taskFlags, task.OtherArgs()) + }) + task.LongDescription("\nUsage: wails3 task [taskname] [flags]\n\nTasks are defined in the `Taskfile.yaml` file. See https://taskfile.dev for more information.") + + generate := app.NewSubCommand("generate", "Generation tools") + generate.NewSubCommandFunction("build-assets", "Generate build assets", commands.GenerateBuildAssets) + generate.NewSubCommandFunction("icons", "Generate icons", commands.GenerateIcons) + generate.NewSubCommandFunction("syso", "Generate Windows .syso file", commands.GenerateSyso) + generate.NewSubCommandFunction("runtime", "Generate the pre-built version of the runtime", commands.GenerateRuntime) + generate.NewSubCommandFunction("webview2bootstrapper", "Generate WebView2 bootstrapper", commands.GenerateWebView2Bootstrapper) + generate.NewSubCommandFunction("template", "Generate a new template", commands.GenerateTemplate) + + update := app.NewSubCommand("update", "Update tools") + update.NewSubCommandFunction("build-assets", "Updates the build assets using the given config file", commands.UpdateBuildAssets) + update.NewSubCommandFunction("cli", "Updates the Wails CLI", commands.UpdateCLI) + + bindgen := generate.NewSubCommand("bindings", "Generate bindings + models") + var bindgenFlags flags.GenerateBindingsOptions + bindgen.AddFlags(&bindgenFlags) + bindgen.Action(func() error { + return commands.GenerateBindings(&bindgenFlags, bindgen.OtherArgs()) + }) + bindgen.LongDescription("\nUsage: wails3 generate bindings [flags] [patterns...]\n\nPatterns match packages to scan for bound types.\nPattern format is analogous to that of the Go build tool,\ne.g. './...' matches packages in the current directory and all descendants.\nIf no pattern is given, the tool will fall back to the current directory.") + generate.NewSubCommandFunction("constants", "Generate JS constants from Go", commands.GenerateConstants) + generate.NewSubCommandFunction(".desktop", "Generate .desktop file", commands.GenerateDotDesktop) + generate.NewSubCommandFunction("appimage", "Generate Linux AppImage", commands.GenerateAppImage) + + plugin := app.NewSubCommand("service", "Service tools") + plugin.NewSubCommandFunction("init", "Initialise a new service", commands.ServiceInit) + + tool := app.NewSubCommand("tool", "Various tools") + tool.NewSubCommandFunction("checkport", "Checks if a port is open. Useful for testing if vite is running.", commands.ToolCheckPort) + tool.NewSubCommandFunction("watcher", "Watches files and runs a command when they change", commands.Watcher) + tool.NewSubCommandFunction("cp", "Copy files", commands.Cp) + tool.NewSubCommandFunction("buildinfo", "Show Build Info", commands.BuildInfo) + tool.NewSubCommandFunction("package", "Generate Linux packages (deb, rpm, archlinux)", commands.ToolPackage) + tool.NewSubCommandFunction("version", "Bump semantic version", commands.ToolVersion) + + app.NewSubCommandFunction("version", "Print the version", commands.Version) + app.NewSubCommand("sponsor", "Sponsor the project").Action(openSponsor) + + defer printFooter() + + err := app.Run() + if err != nil { + pterm.Error.Println(err) + os.Exit(1) + } +} + +func printFooter() { + if !commands.DisableFooter { + docsLink := term.Hyperlink("https://v3.wails.io/getting-started/your-first-app/", "wails3 docs") + + pterm.Println(pterm.LightGreen("\nNeed documentation? Run: ") + pterm.LightBlue(docsLink)) + // Check if we're in a teminal + printer := pterm.PrefixPrinter{ + MessageStyle: pterm.NewStyle(pterm.FgLightGreen), + Prefix: pterm.Prefix{ + Style: pterm.NewStyle(pterm.FgRed, pterm.BgLightWhite), + Text: "♥ ", + }, + } + + linkText := term.Hyperlink("https://github.com/sponsors/leaanthony", "wails3 sponsor") + printer.Println("If Wails is useful to you or your company, please consider sponsoring the project: " + pterm.LightBlue(linkText)) + } +} + +func openDocs() error { + commands.DisableFooter = true + return browser.OpenURL("https://v3.wails.io/getting-started/your-first-app/") +} + +func openSponsor() error { + commands.DisableFooter = true + return browser.OpenURL("https://github.com/sponsors/leaanthony") +} diff --git a/v3/examples/README.md b/v3/examples/README.md new file mode 100644 index 000000000..753ec5138 --- /dev/null +++ b/v3/examples/README.md @@ -0,0 +1,17 @@ +# v3 + +*NOTE*: The examples in this directory may or may not compile / run at any given time during alpha development. + + +## Running the examples + + cd v3/examples/ + go mod tidy + go run . + +## Compiling the examples + + cd v3/examples/ + go mod tidy + go build + ./ diff --git a/v3/examples/badge-custom/README.md b/v3/examples/badge-custom/README.md new file mode 100644 index 000000000..ab4c5a3fb --- /dev/null +++ b/v3/examples/badge-custom/README.md @@ -0,0 +1,128 @@ +# Welcome to Your New Wails3 Project! +Now that you have your project set up, it's time to explore the custom badge features that Wails3 offers on **Windows**. + +## Exploring Custom Badge Features + +### Creating the Service with Custom Options (Windows Only) + +On Windows, you can customize the badge appearance with various options: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/pkg/services/badge" +import "image/color" + +// Create a badge service with custom options +options := badge.Options{ + TextColour: color.RGBA{255, 255, 255, 255}, // White text + BackgroundColour: color.RGBA{0, 0, 255, 255}, // Green background + FontName: "consolab.ttf", // Bold Consolas font + FontSize: 20, // Font size for single character + SmallFontSize: 14, // Font size for multiple characters +} + +badgeService := badge.NewWithOptions(options) + +// Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(badgeService), + }, +}) +``` + +## Badge Operations + +### Setting a Badge + +Set a badge on the application tile/dock icon with the global options applied: + +#### Go +```go +// Set a default badge +badgeService.SetBadge("") + +// Set a numeric badge +badgeService.SetBadge("3") + +// Set a text badge +badgeService.SetBadge("New") +``` + +#### JS +```js +import {SetBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service"; + +// Set a default badge +SetBadge("") + +// Set a numeric badge +SetBadge("3") + +// Set a text badge +SetBadge("New") +``` + +### Setting a Custom Badge + +Set a badge on the application tile/dock icon with one-off options applied: + +#### Go +```go +// Set a default badge +badgeService.SetCustomBadge("") + +// Set a numeric badge +badgeService.SetCustomBadge("3") + +// Set a text badge +badgeService.SetCustomBadge("New") +``` + +#### JS +```js +import {SetCustomBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service"; + +const options = { + BackgroundColour: RGBA.createFrom({ + R: 0, + G: 255, + B: 255, + A: 255, + }), + FontName: "arialb.ttf", // System font + FontSize: 16, + SmallFontSize: 10, + TextColour: RGBA.createFrom({ + R: 0, + G: 0, + B: 0, + A: 255, + }), +} + +// Set a default badge +SetCustomBadge("", options) + +// Set a numeric badge +SetCustomBadge("3", options) + +// Set a text badge +SetCustomBadge("New", options) +``` + +### Removing a Badge + +Remove the badge from the application icon: + +#### Go +```go +badgeService.RemoveBadge() +``` + +#### JS +```js +import {RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service"; + +RemoveBadge() +``` \ No newline at end of file diff --git a/v3/examples/badge-custom/Taskfile.yml b/v3/examples/badge-custom/Taskfile.yml new file mode 100644 index 000000000..d1bcfaacf --- /dev/null +++ b/v3/examples/badge-custom/Taskfile.yml @@ -0,0 +1,34 @@ +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "badge" + BIN_DIR: "bin" + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + diff --git a/v3/examples/badge-custom/build/Taskfile.yml b/v3/examples/badge-custom/build/Taskfile.yml new file mode 100644 index 000000000..5f3517efc --- /dev/null +++ b/v3/examples/badge-custom/build/Taskfile.yml @@ -0,0 +1,86 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + 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 + + build:frontend: + label: build:frontend (PRODUCTION={{.PRODUCTION}}) + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/**/* + deps: + - task: install:frontend:deps + - task: generate:bindings + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + cmds: + - npm run {{.BUILD_COMMAND}} -q + env: + PRODUCTION: '{{.PRODUCTION | default "false"}}' + vars: + BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build:dev{{end}}' + + + generate:bindings: + label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}}) + summary: Generates bindings for the frontend + 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 -ts + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + sources: + - "appicon.png" + generates: + - "darwin/icons.icns" + - "windows/icon.ico" + cmds: + - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico + + dev:frontend: + summary: Runs the frontend in development mode + dir: frontend + deps: + - task: install:frontend:deps + cmds: + - npm 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 . diff --git a/v3/examples/badge-custom/build/appicon.png b/v3/examples/badge-custom/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/badge-custom/build/appicon.png differ diff --git a/v3/examples/badge-custom/build/config.yml b/v3/examples/badge-custom/build/config.yml new file mode 100644 index 000000000..aaa3240fb --- /dev/null +++ b/v3/examples/badge-custom/build/config.yml @@ -0,0 +1,63 @@ +# 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: "My Company" # The name of the company + productName: "My Product" # The name of the application + productIdentifier: "com.mycompany.myproduct" # The unique product identifier + description: "A program that does X" # The application description + copyright: "(c) 2025, My Company" # Copyright text + comments: "Some Product Comments" # Comments + version: "0.0.1" # The application version + +# Dev mode configuration +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - 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/v3/examples/badge-custom/build/darwin/Info.dev.plist b/v3/examples/badge-custom/build/darwin/Info.dev.plist new file mode 100644 index 000000000..4caddf720 --- /dev/null +++ b/v3/examples/badge-custom/build/darwin/Info.dev.plist @@ -0,0 +1,32 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + badge + CFBundleIdentifier + com.wails.badge + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/v3/examples/badge-custom/build/darwin/Info.plist b/v3/examples/badge-custom/build/darwin/Info.plist new file mode 100644 index 000000000..0dc90b2e7 --- /dev/null +++ b/v3/examples/badge-custom/build/darwin/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + badge + CFBundleIdentifier + com.wails.badge + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + + \ No newline at end of file diff --git a/v3/examples/badge-custom/build/darwin/Taskfile.yml b/v3/examples/badge-custom/build/darwin/Taskfile.yml new file mode 100644 index 000000000..f0791fea9 --- /dev/null +++ b/v3/examples/badge-custom/build/darwin/Taskfile.yml @@ -0,0 +1,81 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Creates a production build of the application + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.OUTPUT}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{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=10.15" + CGO_LDFLAGS: "-mmacosx-version-min=10.15" + MACOSX_DEPLOYMENT_TARGET: "10.15" + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + 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: + - 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" + + package: + summary: Packages a production build of the application into a `.app` bundle + deps: + - task: build + vars: + PRODUCTION: "true" + 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,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS + - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents + - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app + + run: + cmds: + - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/{MacOS,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources + - 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}}' diff --git a/v3/examples/badge-custom/build/darwin/icons.icns b/v3/examples/badge-custom/build/darwin/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/badge-custom/build/darwin/icons.icns differ diff --git a/v3/examples/badge-custom/build/linux/Taskfile.yml b/v3/examples/badge-custom/build/linux/Taskfile.yml new file mode 100644 index 000000000..560cc9c92 --- /dev/null +++ b/v3/examples/badge-custom/build/linux/Taskfile.yml @@ -0,0 +1,119 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Linux + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application for Linux + deps: + - task: build + vars: + PRODUCTION: "true" + 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 + vars: + PRODUCTION: "true" + - task: generate:dotdesktop + cmds: + - cp {{.APP_BINARY}} {{.APP_NAME}} + - cp ../../appicon.png appicon.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: '../../appicon.png' + DESKTOP_FILE: '../{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../../bin' + + create:deb: + summary: Creates a deb package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:deb + + create:rpm: + summary: Creates a rpm package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:rpm + + create:aur: + summary: Creates a arch linux packager package + deps: + - task: build + vars: + PRODUCTION: "true" + 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: 'appicon' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/badge-custom/build/linux/appimage/build.sh b/v3/examples/badge-custom/build/linux/appimage/build.sh new file mode 100644 index 000000000..85901c34e --- /dev/null +++ b/v3/examples/badge-custom/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/v3/examples/badge-custom/build/linux/nfpm/nfpm.yaml b/v3/examples/badge-custom/build/linux/nfpm/nfpm.yaml new file mode 100644 index 000000000..5b7ea9d02 --- /dev/null +++ b/v3/examples/badge-custom/build/linux/nfpm/nfpm.yaml @@ -0,0 +1,50 @@ +# 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: "badge" +arch: ${GOARCH} +platform: "linux" +version: "0.1.0" +section: "default" +priority: "extra" +maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> +description: "My Product Description" +vendor: "My Company" +homepage: "https://wails.io" +license: "MIT" +release: "1" + +contents: + - src: "./bin/badge" + dst: "/usr/local/bin/badge" + - src: "./build/appicon.png" + dst: "/usr/share/icons/hicolor/128x128/apps/badge.png" + - src: "./build/linux/badge.desktop" + dst: "/usr/share/applications/badge.desktop" + +depends: + - gtk3 + - libwebkit2gtk + +# replaces: +# - foobar +# provides: +# - bar +# depends: +# - gtk3 +# - libwebkit2gtk +# recommends: +# - whatever +# suggests: +# - something-else +# conflicts: +# - not-foo +# - not-bar +# changelog: "changelog.yaml" +# scripts: +# preinstall: ./build/linux/nfpm/scripts/preinstall.sh +# postinstall: ./build/linux/nfpm/scripts/postinstall.sh +# preremove: ./build/linux/nfpm/scripts/preremove.sh +# postremove: ./build/linux/nfpm/scripts/postremove.sh diff --git a/v3/examples/badge-custom/build/linux/nfpm/scripts/postinstall.sh b/v3/examples/badge-custom/build/linux/nfpm/scripts/postinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge-custom/build/linux/nfpm/scripts/postinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge-custom/build/linux/nfpm/scripts/postremove.sh b/v3/examples/badge-custom/build/linux/nfpm/scripts/postremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge-custom/build/linux/nfpm/scripts/postremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge-custom/build/linux/nfpm/scripts/preinstall.sh b/v3/examples/badge-custom/build/linux/nfpm/scripts/preinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge-custom/build/linux/nfpm/scripts/preinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge-custom/build/linux/nfpm/scripts/preremove.sh b/v3/examples/badge-custom/build/linux/nfpm/scripts/preremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge-custom/build/linux/nfpm/scripts/preremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge-custom/build/windows/Taskfile.yml b/v3/examples/badge-custom/build/windows/Taskfile.yml new file mode 100644 index 000000000..534f4fb31 --- /dev/null +++ b/v3/examples/badge-custom/build/windows/Taskfile.yml @@ -0,0 +1,63 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Windows + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - task: generate:syso + - go build {{.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 .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: windows + CGO_ENABLED: 0 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application into a `.exe` bundle + cmds: + - task: create:nsis:installer + + 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 + vars: + PRODUCTION: "true" + cmds: + # Create the Microsoft WebView2 bootstrapper if it doesn't exist + - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis" + - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}.exe' diff --git a/v3/examples/badge-custom/build/windows/icon.ico b/v3/examples/badge-custom/build/windows/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/badge-custom/build/windows/icon.ico differ diff --git a/v3/examples/badge-custom/build/windows/info.json b/v3/examples/badge-custom/build/windows/info.json new file mode 100644 index 000000000..850b2b5b0 --- /dev/null +++ b/v3/examples/badge-custom/build/windows/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.1.0" + }, + "info": { + "0000": { + "ProductVersion": "0.1.0", + "CompanyName": "My Company", + "FileDescription": "My Product Description", + "LegalCopyright": "© now, My Company", + "ProductName": "My Product", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/badge-custom/build/windows/nsis/project.nsi b/v3/examples/badge-custom/build/windows/nsis/project.nsi new file mode 100644 index 000000000..985b8e207 --- /dev/null +++ b/v3/examples/badge-custom/build/windows/nsis/project.nsi @@ -0,0 +1,112 @@ +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 "badge" +## !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 "© now, 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 +#### +## 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. +InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). +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.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.deleteUninstaller +SectionEnd diff --git a/v3/examples/badge-custom/build/windows/nsis/wails_tools.nsh b/v3/examples/badge-custom/build/windows/nsis/wails_tools.nsh new file mode 100644 index 000000000..6fc10ab79 --- /dev/null +++ b/v3/examples/badge-custom/build/windows/nsis/wails_tools.nsh @@ -0,0 +1,212 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "badge" +!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 "© now, 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 REQUEST_EXECUTION_LEVEL + !define REQUEST_EXECUTION_LEVEL "admin" +!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 + 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" +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + DeleteRegKey HKLM "${UNINST_KEY}" +!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 \ No newline at end of file diff --git a/v3/examples/badge-custom/build/windows/wails.exe.manifest b/v3/examples/badge-custom/build/windows/wails.exe.manifest new file mode 100644 index 000000000..1d8992a3d --- /dev/null +++ b/v3/examples/badge-custom/build/windows/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v3/examples/badge-custom/frontend/Inter Font License.txt b/v3/examples/badge-custom/frontend/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/examples/badge-custom/frontend/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts new file mode 100644 index 000000000..f959d50dc --- /dev/null +++ b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts @@ -0,0 +1,33 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service represents the notifications service + * @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"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * RemoveBadge removes the badge label from the application icon. + */ +export function RemoveBadge(): $CancellablePromise { + return $Call.ByID(2374916939); +} + +/** + * SetBadge sets the badge label on the application icon. + */ +export function SetBadge(label: string): $CancellablePromise { + return $Call.ByID(784276339, label); +} + +export function SetCustomBadge(label: string, options: $models.Options): $CancellablePromise { + return $Call.ByID(3058653106, label, options); +} diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts new file mode 100644 index 000000000..df3ea9723 --- /dev/null +++ b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as BadgeService from "./badgeservice.js"; +export { + BadgeService +}; + +export { + Options +} from "./models.js"; diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts new file mode 100644 index 000000000..67ed264c0 --- /dev/null +++ b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts @@ -0,0 +1,58 @@ +// 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"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as color$0 from "../../../../../../../image/color/models.js"; + +export class Options { + "TextColour": color$0.RGBA; + "BackgroundColour": color$0.RGBA; + "FontName": string; + "FontSize": number; + "SmallFontSize": number; + + /** Creates a new Options instance. */ + constructor($$source: Partial = {}) { + if (!("TextColour" in $$source)) { + this["TextColour"] = (new color$0.RGBA()); + } + if (!("BackgroundColour" in $$source)) { + this["BackgroundColour"] = (new color$0.RGBA()); + } + if (!("FontName" in $$source)) { + this["FontName"] = ""; + } + if (!("FontSize" in $$source)) { + this["FontSize"] = 0; + } + if (!("SmallFontSize" in $$source)) { + this["SmallFontSize"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Options instance from a string or object. + */ + static createFrom($$source: any = {}): Options { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("TextColour" in $$parsedSource) { + $$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]); + } + if ("BackgroundColour" in $$parsedSource) { + $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); + } + return new Options($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = color$0.RGBA.createFrom; diff --git a/v3/examples/badge-custom/frontend/bindings/image/color/index.ts b/v3/examples/badge-custom/frontend/bindings/image/color/index.ts new file mode 100644 index 000000000..97b507b08 --- /dev/null +++ b/v3/examples/badge-custom/frontend/bindings/image/color/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + RGBA +} from "./models.js"; diff --git a/v3/examples/badge-custom/frontend/bindings/image/color/models.ts b/v3/examples/badge-custom/frontend/bindings/image/color/models.ts new file mode 100644 index 000000000..0d4eab56d --- /dev/null +++ b/v3/examples/badge-custom/frontend/bindings/image/color/models.ts @@ -0,0 +1,46 @@ +// 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"; + +/** + * RGBA represents a traditional 32-bit alpha-premultiplied color, having 8 + * bits for each of red, green, blue and alpha. + * + * An alpha-premultiplied color component C has been scaled by alpha (A), so + * has valid values 0 <= C <= A. + */ +export class RGBA { + "R": number; + "G": number; + "B": number; + "A": number; + + /** Creates a new RGBA instance. */ + constructor($$source: Partial = {}) { + if (!("R" in $$source)) { + this["R"] = 0; + } + if (!("G" in $$source)) { + this["G"] = 0; + } + if (!("B" in $$source)) { + this["B"] = 0; + } + if (!("A" in $$source)) { + this["A"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new RGBA instance from a string or object. + */ + static createFrom($$source: any = {}): RGBA { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new RGBA($$parsedSource as Partial); + } +} diff --git a/v3/examples/badge-custom/frontend/dist/Inter-Medium.ttf b/v3/examples/badge-custom/frontend/dist/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/badge-custom/frontend/dist/Inter-Medium.ttf differ diff --git a/v3/examples/badge-custom/frontend/dist/assets/index--TgUfkZn.js b/v3/examples/badge-custom/frontend/dist/assets/index--TgUfkZn.js new file mode 100644 index 000000000..6d342144b --- /dev/null +++ b/v3/examples/badge-custom/frontend/dist/assets/index--TgUfkZn.js @@ -0,0 +1,6 @@ +var ue=Object.defineProperty;var de=(t,e,n)=>e in t?ue(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var j=(t,e,n)=>de(t,typeof e!="symbol"?e+"":e,n);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(o){if(o.ep)return;o.ep=!0;const i=n(o);fetch(o.href,i)}})();const fe="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function te(t=21){let e="",n=t|0;for(;n--;)e+=fe[Math.random()*64|0];return e}const we=window.location.origin+"/wails/runtime",z=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let pe=te();function R(t,e=""){return function(n,r=null){return me(t,n,e,r)}}async function me(t,e,n,r){var o,i;let s=new URL(we);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),r&&s.searchParams.append("args",JSON.stringify(r));let c={"x-wails-client-id":pe};n&&(c["x-wails-window-name"]=n);let l=await fetch(s,{headers:c});if(!l.ok)throw new Error(await l.text());return((i=(o=l.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?l.json():l.text()}R(z.System);const P=function(){var t,e,n,r,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(r=(n=window.webkit)===null||n===void 0?void 0:n.messageHandlers)===null||r===void 0?void 0:r.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` +%c⚠️ Browser Environment Detected %c + +%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. +More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development +`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function k(t){P==null||P(t)}function ne(){return window._wails.environment.OS==="windows"}function he(){return!!window._wails.environment.Debug}function ge(){return new MouseEvent("mousedown").buttons===0}function re(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",Ee);const ye=R(z.ContextMenu),be=0;function ve(t,e,n,r){ye(be,{id:t,x:e,y:n,data:r})}function Ee(t){const e=re(t),n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(n){t.preventDefault();const r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");ve(n,t.clientX,t.clientY,r)}else je(t,e)}function je(t,e){if(he())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const n=window.getSelection(),r=n&&n.toString().length>0;if(r)for(let o=0;o{U=t,U||(v=E=!1,u())};window.addEventListener("mousedown",X,{capture:!0});window.addEventListener("mousemove",X,{capture:!0});window.addEventListener("mouseup",X,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,Se,{capture:!0});function Se(t){(C||E)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const F=0,Ce=1,I=2;function X(t){let e,n=t.buttons;switch(t.type){case"mousedown":e=F,D||(n=h|1<"u"||typeof e=="object"))try{var n=L.call(e);return(n===Te||n===Ae||n===Pe||n===Le)&&e("")==null}catch{}return!1})}function Ie(t){if(J(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{y(t,null,N)}catch(e){if(e!==O)return!1}return!Y(t)&&W(t)}function _e(t){if(J(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(He)return W(t);if(Y(t))return!1;var e=L.call(t);return e!==ke&&e!==Be&&!/^\[object HTML/.test(e)?!1:W(t)}const m=y?Ie:_e;var _;class G extends Error{constructor(e,n){super(e,n),this.name="CancelError"}}class x extends Error{constructor(e,n,r){super((r??"Unhandled rejection in cancelled promise.")+" Reason: "+Ue(n),{cause:n}),this.promise=e,this.name="CancelledRejectionError"}}const f=Symbol("barrier"),Q=Symbol("cancelImpl"),Z=(_=Symbol.species)!==null&&_!==void 0?_:Symbol("speciesPolyfill");class a extends Promise{constructor(e,n){let r,o;if(super((l,d)=>{r=l,o=d}),this.constructor[Z]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:r,reject:o,get oncancelled(){return n??null},set oncancelled(l){n=l??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[f]:{configurable:!1,enumerable:!1,writable:!0,value:null},[Q]:{configurable:!1,enumerable:!1,writable:!1,value:ie(i,s)}});const c=le(i,s);try{e(se(i,s),c)}catch(l){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",l):c(l)}}cancel(e){return new a(n=>{Promise.all([this[Q](new G("Promise cancelled.",{cause:e})),Ne(this)]).then(()=>n(),()=>n())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,n,r){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(m(e)||(e=$),m(n)||(n=ee),e===$&&n==ee)return new a(i=>i(this));const o={};return this[f]=o,new a((i,s)=>{super.then(c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(e(c))}catch(d){s(d)}},c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(n(c))}catch(d){s(d)}})},async i=>{try{return r==null?void 0:r(i)}finally{await this.cancel(i)}})}catch(e,n){return this.then(void 0,e,n)}finally(e,n){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return m(e)?this.then(r=>a.resolve(e()).then(()=>r),r=>a.resolve(e()).then(()=>{throw r}),n):this.then(e,e,n)}static get[Z](){return Promise}static all(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.all(n).then(o,i)},o=>M(r,n,o));return r}static allSettled(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.allSettled(n).then(o,i)},o=>M(r,n,o));return r}static any(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.any(n).then(o,i)},o=>M(r,n,o));return r}static race(e){let n=Array.from(e);const r=new a((o,i)=>{Promise.race(n).then(o,i)},o=>M(r,n,o));return r}static cancel(e){const n=new a(()=>{});return n.cancel(e),n}static timeout(e,n){const r=new a(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void r.cancel(n)):setTimeout(()=>void r.cancel(n),e),r}static sleep(e,n){return new a(r=>{setTimeout(()=>r(n),e)})}static reject(e){return new a((n,r)=>r(e))}static resolve(e){return e instanceof a?e:new a(n=>n(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new a((n,r)=>{e.resolve=n,e.reject=r},n=>{var r;(r=e.oncancelled)===null||r===void 0||r.call(e,n)}),e}}function ie(t,e){let n;return r=>{if(e.settled||(e.settled=!0,e.reason=r,t.reject(r),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==r)throw o})),!(!e.reason||!t.oncancelled))return n=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new x(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new x(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,n}}function se(t,e){return n=>{if(!e.resolving){if(e.resolving=!0,n===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(n!=null&&(typeof n=="object"||typeof n=="function")){let r;try{r=n.then}catch(o){e.settled=!0,t.reject(o);return}if(m(r)){try{let s=n.cancel;if(m(s)){const c=l=>{Reflect.apply(s,n,[l])};e.reason?ie(Object.assign(Object.assign({},t),{oncancelled:c}),e)(e.reason):t.oncancelled=c}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=le(t,o);try{Reflect.apply(r,n,[se(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(n))}}}function le(t,e){return n=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(n instanceof G&&e.reason instanceof G&&Object.is(n.cause,e.reason.cause))return}catch{}Promise.reject(new x(t.promise,n))}else e.settled=!0,t.reject(n)}}function M(t,e,n){const r=[];for(const o of e){let i;try{if(!m(o.then)||(i=o.cancel,!m(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[n])}catch(c){Promise.reject(new x(t,c,"Unhandled exception in cancel method."));continue}s&&r.push((s instanceof Promise?s:Promise.resolve(s)).catch(c=>{Promise.reject(new x(t,c,"Unhandled rejection in cancel method."))}))}return Promise.all(r)}function $(t){return t}function ee(t){throw t}function Ue(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function Ne(t){var e;let n=(e=t[f])!==null&&e!==void 0?e:{};return"promise"in n||Object.assign(n,g()),t[f]==null&&(n.resolve(),t[f]=n),n.promise}let g=Promise.withResolvers;g&&typeof g=="function"?g=g.bind(Promise):g=function(){let t,e;return{promise:new Promise((r,o)=>{t=r,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Ve;window._wails.callErrorHandler=qe;const We=R(z.Call),Ge=R(z.CancelCall),b=new Map,Xe=0,Ye=0;class Je extends Error{constructor(e,n){super(e,n),this.name="RuntimeError"}}function Ve(t,e,n){const r=ce(t);if(r)if(!e)r.resolve(void 0);else if(!n)r.resolve(e);else try{r.resolve(JSON.parse(e))}catch(o){r.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function qe(t,e,n){const r=ce(t);if(r)if(!n)r.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(c){r.reject(new TypeError("could not parse error: "+c.message,{cause:c}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new Je(o.message,i);break;default:s=new Error(o.message,i);break}r.reject(s)}}function ce(t){const e=b.get(t);return b.delete(t),e}function Ke(){let t;do t=te();while(b.has(t));return t}function Qe(t){const e=Ke(),n=a.withResolvers();b.set(e,{resolve:n.resolve,reject:n.reject});const r=We(Xe,Object.assign({"call-id":e},t));let o=!1;r.then(()=>{o=!0},s=>{b.delete(e),n.reject(s)});const i=()=>(b.delete(e),Ge(Ye,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return n.oncancelled=()=>o?i():r.then(i),n.promise}function V(t,...e){return Qe({methodID:t,args:e})}const w=new Map;class Ze{constructor(e,n,r){this.eventName=e,this.callback=n,this.maxCallbacks=r||-1}dispatch(e){try{this.callback(e)}catch(n){console.error(n)}return this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0)}}function $e(t){let e=w.get(t.eventName);e&&(e=e.filter(n=>n!==t),e.length===0?w.delete(t.eventName):w.set(t.eventName,e))}window._wails=window._wails||{};window._wails.dispatchWailsEvent=rt;const et=R(z.Events),tt=0;class nt{constructor(e,n=null){this.name=e,this.data=n}}function rt(t){let e=w.get(t.name);if(!e)return;let n=new nt(t.name,t.data);"sender"in t&&(n.sender=t.sender),e=e.filter(r=>!r.dispatch(n)),e.length===0?w.delete(t.name):w.set(t.name,e)}function ot(t,e,n){let r=w.get(t)||[];const o=new Ze(t,e,n);return r.push(o),w.set(t,r),()=>$e(o)}function it(t,e){return ot(t,e,-1)}function ae(t){return et(tt,t)}window._wails=window._wails||{};window._wails.invoke=k;k("wails:runtime:ready");function st(){return V(2374916939)}function lt(t){return V(784276339,t)}function ct(t,e){return V(3058653106,t,e)}class B{constructor(e={}){j(this,"R");j(this,"G");j(this,"B");j(this,"A");"R"in e||(this.R=0),"G"in e||(this.G=0),"B"in e||(this.B=0),"A"in e||(this.A=0),Object.assign(this,e)}static createFrom(e={}){let n=typeof e=="string"?JSON.parse(e):e;return new B(n)}}const at=document.getElementById("set-custom"),ut=document.getElementById("set"),dt=document.getElementById("remove"),ft=document.getElementById("set-go"),wt=document.getElementById("remove-go"),q=document.getElementById("label"),pt=document.getElementById("time");at.addEventListener("click",()=>{console.log("click!");let t=q.value;ct(t,{BackgroundColour:B.createFrom({R:0,G:255,B:255,A:255}),FontName:"arialb.ttf",FontSize:16,SmallFontSize:10,TextColour:B.createFrom({R:0,G:0,B:0,A:255})})});ut.addEventListener("click",()=>{let t=q.value;lt(t)});dt.addEventListener("click",()=>{st()});ft.addEventListener("click",()=>{let t=q.value;ae({name:"set:badge",data:t})});wt.addEventListener("click",()=>{ae({name:"remove:badge",data:null})});it("time",t=>{pt.innerText=t.data}); diff --git a/v3/examples/badge-custom/frontend/dist/index.html b/v3/examples/badge-custom/frontend/dist/index.html new file mode 100644 index 000000000..d785892ac --- /dev/null +++ b/v3/examples/badge-custom/frontend/dist/index.html @@ -0,0 +1,39 @@ + + + + + + + + Wails App + + + +
+ +

Wails + Typescript

+
Set a badge label below 👇
+
+
+ + + + + + +
+
+ +
+ + diff --git a/v3/examples/badge-custom/frontend/dist/style.css b/v3/examples/badge-custom/frontend/dist/style.css new file mode 100644 index 000000000..6ce81cad2 --- /dev/null +++ b/v3/examples/badge-custom/frontend/dist/style.css @@ -0,0 +1,155 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/badge-custom/frontend/dist/typescript.svg b/v3/examples/badge-custom/frontend/dist/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/badge-custom/frontend/dist/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/badge-custom/frontend/dist/wails.png b/v3/examples/badge-custom/frontend/dist/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/badge-custom/frontend/dist/wails.png differ diff --git a/v3/examples/badge-custom/frontend/index.html b/v3/examples/badge-custom/frontend/index.html new file mode 100644 index 000000000..e4a9ec4a2 --- /dev/null +++ b/v3/examples/badge-custom/frontend/index.html @@ -0,0 +1,39 @@ + + + + + + + + Wails App + + +
+ +

Wails + Typescript

+
Set a badge label below 👇
+
+
+ + + + + + +
+
+ +
+ + + diff --git a/v3/examples/badge-custom/frontend/package.json b/v3/examples/badge-custom/frontend/package.json new file mode 100644 index 000000000..b39da7ece --- /dev/null +++ b/v3/examples/badge-custom/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^5.0.0" + } +} diff --git a/v3/examples/badge-custom/frontend/public/Inter-Medium.ttf b/v3/examples/badge-custom/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/badge-custom/frontend/public/Inter-Medium.ttf differ diff --git a/v3/examples/badge-custom/frontend/public/style.css b/v3/examples/badge-custom/frontend/public/style.css new file mode 100644 index 000000000..6ce81cad2 --- /dev/null +++ b/v3/examples/badge-custom/frontend/public/style.css @@ -0,0 +1,155 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/badge-custom/frontend/public/typescript.svg b/v3/examples/badge-custom/frontend/public/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/badge-custom/frontend/public/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/badge-custom/frontend/public/wails.png b/v3/examples/badge-custom/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/badge-custom/frontend/public/wails.png differ diff --git a/v3/examples/badge-custom/frontend/src/main.ts b/v3/examples/badge-custom/frontend/src/main.ts new file mode 100644 index 000000000..405f4fe28 --- /dev/null +++ b/v3/examples/badge-custom/frontend/src/main.ts @@ -0,0 +1,59 @@ +import {Events} from "@wailsio/runtime"; +import {SetBadge, RemoveBadge, SetCustomBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice"; +import { RGBA } from "../bindings/image/color/models"; + +const setCustomButton = document.getElementById('set-custom')! as HTMLButtonElement; +const setButton = document.getElementById('set')! as HTMLButtonElement; +const removeButton = document.getElementById('remove')! as HTMLButtonElement; +const setButtonUsingGo = document.getElementById('set-go')! as HTMLButtonElement; +const removeButtonUsingGo = document.getElementById('remove-go')! as HTMLButtonElement; +const labelElement : HTMLInputElement = document.getElementById('label')! as HTMLInputElement; +const timeElement = document.getElementById('time')! as HTMLDivElement; + +setCustomButton.addEventListener('click', () => { + console.log("click!") + let label = (labelElement as HTMLInputElement).value + SetCustomBadge(label, { + BackgroundColour: RGBA.createFrom({ + R: 0, + G: 255, + B: 255, + A: 255, + }), + FontName: "arialb.ttf", // System font + FontSize: 16, + SmallFontSize: 10, + TextColour: RGBA.createFrom({ + R: 0, + G: 0, + B: 0, + A: 255, + }), + }); +}) + +setButton.addEventListener('click', () => { + let label = (labelElement as HTMLInputElement).value + SetBadge(label); +}); + +removeButton.addEventListener('click', () => { + RemoveBadge(); +}); + +setButtonUsingGo.addEventListener('click', () => { + let label = (labelElement as HTMLInputElement).value + void Events.Emit({ + name: "set:badge", + data: label, + }) +}) + +removeButtonUsingGo.addEventListener('click', () => { + void Events.Emit({name:"remove:badge", data: null}) +}) + +Events.On('time', (time: {data: any}) => { + timeElement.innerText = time.data; +}); + diff --git a/v3/examples/badge-custom/frontend/src/vite-env.d.ts b/v3/examples/badge-custom/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/examples/badge-custom/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/examples/badge-custom/frontend/tsconfig.json b/v3/examples/badge-custom/frontend/tsconfig.json new file mode 100644 index 000000000..c267ecf24 --- /dev/null +++ b/v3/examples/badge-custom/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/v3/examples/badge-custom/main.go b/v3/examples/badge-custom/main.go new file mode 100644 index 000000000..e27103443 --- /dev/null +++ b/v3/examples/badge-custom/main.go @@ -0,0 +1,108 @@ +package main + +import ( + "embed" + _ "embed" + "image/color" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/services/badge" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed all:frontend/dist +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + + badgeService := badge.NewWithOptions(badge.Options{ + TextColour: color.RGBA{255, 255, 204, 255}, + BackgroundColour: color.RGBA{16, 124, 16, 255}, + FontName: "consolab.ttf", + FontSize: 20, + SmallFontSize: 14, + }) + + app := application.New(application.Options{ + Name: "badge", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(badgeService), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + app.Event.On("remove:badge", func(event *application.CustomEvent) { + err := badgeService.RemoveBadge() + if err != nil { + log.Fatal(err) + } + }) + + app.Event.On("set:badge", func(event *application.CustomEvent) { + text := event.Data.(string) + err := badgeService.SetBadge(text) + if err != nil { + log.Fatal(err) + } + }) + + // Create a goroutine that emits an event containing the current time every second. + // The frontend can listen to this event and update the UI accordingly. + go func() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + now := time.Now().Format(time.RFC1123) + app.Event.Emit("time", now) + case <-app.Context().Done(): + return + } + } + }() + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/badge/README.md b/v3/examples/badge/README.md new file mode 100644 index 000000000..abb7b9653 --- /dev/null +++ b/v3/examples/badge/README.md @@ -0,0 +1,71 @@ +# Welcome to Your New Wails3 Project! +Now that you have your project set up, it's time to explore the basic badge features that Wails3 offers on **macOS** and **Windows**. + +## Exploring Badge Features + +### Creating the Service + +First, initialize the badge service: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/pkg/services/badge" + +// Create a new badge service +badgeService := badge.New() + +// Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(badgeService), + }, +}) +``` + +## Badge Operations + +### Setting a Badge + +Set a badge on the application tile/dock icon: + +#### Go +```go +// Set a default badge +badgeService.SetBadge("") + +// Set a numeric badge +badgeService.SetBadge("3") + +// Set a text badge +badgeService.SetBadge("New") +``` + +#### JS +```js +import {SetBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service"; + +// Set a default badge +SetBadge("") + +// Set a numeric badge +SetBadge("3") + +// Set a text badge +SetBadge("New") +``` + +### Removing a Badge + +Remove the badge from the application icon: + +#### Go +```go +badgeService.RemoveBadge() +``` + +#### JS +```js +import {RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service"; + +RemoveBadge() +``` \ No newline at end of file diff --git a/v3/examples/badge/Taskfile.yml b/v3/examples/badge/Taskfile.yml new file mode 100644 index 000000000..d1bcfaacf --- /dev/null +++ b/v3/examples/badge/Taskfile.yml @@ -0,0 +1,34 @@ +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "badge" + BIN_DIR: "bin" + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + diff --git a/v3/examples/badge/build/Taskfile.yml b/v3/examples/badge/build/Taskfile.yml new file mode 100644 index 000000000..5f3517efc --- /dev/null +++ b/v3/examples/badge/build/Taskfile.yml @@ -0,0 +1,86 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + 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 + + build:frontend: + label: build:frontend (PRODUCTION={{.PRODUCTION}}) + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/**/* + deps: + - task: install:frontend:deps + - task: generate:bindings + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + cmds: + - npm run {{.BUILD_COMMAND}} -q + env: + PRODUCTION: '{{.PRODUCTION | default "false"}}' + vars: + BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build:dev{{end}}' + + + generate:bindings: + label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}}) + summary: Generates bindings for the frontend + 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 -ts + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + sources: + - "appicon.png" + generates: + - "darwin/icons.icns" + - "windows/icon.ico" + cmds: + - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico + + dev:frontend: + summary: Runs the frontend in development mode + dir: frontend + deps: + - task: install:frontend:deps + cmds: + - npm 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 . diff --git a/v3/examples/badge/build/appicon.png b/v3/examples/badge/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/badge/build/appicon.png differ diff --git a/v3/examples/badge/build/config.yml b/v3/examples/badge/build/config.yml new file mode 100644 index 000000000..aaa3240fb --- /dev/null +++ b/v3/examples/badge/build/config.yml @@ -0,0 +1,63 @@ +# 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: "My Company" # The name of the company + productName: "My Product" # The name of the application + productIdentifier: "com.mycompany.myproduct" # The unique product identifier + description: "A program that does X" # The application description + copyright: "(c) 2025, My Company" # Copyright text + comments: "Some Product Comments" # Comments + version: "0.0.1" # The application version + +# Dev mode configuration +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - 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/v3/examples/badge/build/darwin/Info.dev.plist b/v3/examples/badge/build/darwin/Info.dev.plist new file mode 100644 index 000000000..4caddf720 --- /dev/null +++ b/v3/examples/badge/build/darwin/Info.dev.plist @@ -0,0 +1,32 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + badge + CFBundleIdentifier + com.wails.badge + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/v3/examples/badge/build/darwin/Info.plist b/v3/examples/badge/build/darwin/Info.plist new file mode 100644 index 000000000..0dc90b2e7 --- /dev/null +++ b/v3/examples/badge/build/darwin/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + badge + CFBundleIdentifier + com.wails.badge + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + + \ No newline at end of file diff --git a/v3/examples/badge/build/darwin/Taskfile.yml b/v3/examples/badge/build/darwin/Taskfile.yml new file mode 100644 index 000000000..f0791fea9 --- /dev/null +++ b/v3/examples/badge/build/darwin/Taskfile.yml @@ -0,0 +1,81 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Creates a production build of the application + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.OUTPUT}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{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=10.15" + CGO_LDFLAGS: "-mmacosx-version-min=10.15" + MACOSX_DEPLOYMENT_TARGET: "10.15" + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + 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: + - 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" + + package: + summary: Packages a production build of the application into a `.app` bundle + deps: + - task: build + vars: + PRODUCTION: "true" + 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,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS + - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents + - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app + + run: + cmds: + - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/{MacOS,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources + - 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}}' diff --git a/v3/examples/badge/build/darwin/icons.icns b/v3/examples/badge/build/darwin/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/badge/build/darwin/icons.icns differ diff --git a/v3/examples/badge/build/linux/Taskfile.yml b/v3/examples/badge/build/linux/Taskfile.yml new file mode 100644 index 000000000..560cc9c92 --- /dev/null +++ b/v3/examples/badge/build/linux/Taskfile.yml @@ -0,0 +1,119 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Linux + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application for Linux + deps: + - task: build + vars: + PRODUCTION: "true" + 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 + vars: + PRODUCTION: "true" + - task: generate:dotdesktop + cmds: + - cp {{.APP_BINARY}} {{.APP_NAME}} + - cp ../../appicon.png appicon.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: '../../appicon.png' + DESKTOP_FILE: '../{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../../bin' + + create:deb: + summary: Creates a deb package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:deb + + create:rpm: + summary: Creates a rpm package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:rpm + + create:aur: + summary: Creates a arch linux packager package + deps: + - task: build + vars: + PRODUCTION: "true" + 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: 'appicon' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/badge/build/linux/appimage/build.sh b/v3/examples/badge/build/linux/appimage/build.sh new file mode 100644 index 000000000..85901c34e --- /dev/null +++ b/v3/examples/badge/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/v3/examples/badge/build/linux/nfpm/nfpm.yaml b/v3/examples/badge/build/linux/nfpm/nfpm.yaml new file mode 100644 index 000000000..5b7ea9d02 --- /dev/null +++ b/v3/examples/badge/build/linux/nfpm/nfpm.yaml @@ -0,0 +1,50 @@ +# 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: "badge" +arch: ${GOARCH} +platform: "linux" +version: "0.1.0" +section: "default" +priority: "extra" +maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> +description: "My Product Description" +vendor: "My Company" +homepage: "https://wails.io" +license: "MIT" +release: "1" + +contents: + - src: "./bin/badge" + dst: "/usr/local/bin/badge" + - src: "./build/appicon.png" + dst: "/usr/share/icons/hicolor/128x128/apps/badge.png" + - src: "./build/linux/badge.desktop" + dst: "/usr/share/applications/badge.desktop" + +depends: + - gtk3 + - libwebkit2gtk + +# replaces: +# - foobar +# provides: +# - bar +# depends: +# - gtk3 +# - libwebkit2gtk +# recommends: +# - whatever +# suggests: +# - something-else +# conflicts: +# - not-foo +# - not-bar +# changelog: "changelog.yaml" +# scripts: +# preinstall: ./build/linux/nfpm/scripts/preinstall.sh +# postinstall: ./build/linux/nfpm/scripts/postinstall.sh +# preremove: ./build/linux/nfpm/scripts/preremove.sh +# postremove: ./build/linux/nfpm/scripts/postremove.sh diff --git a/v3/examples/badge/build/linux/nfpm/scripts/postinstall.sh b/v3/examples/badge/build/linux/nfpm/scripts/postinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge/build/linux/nfpm/scripts/postinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge/build/linux/nfpm/scripts/postremove.sh b/v3/examples/badge/build/linux/nfpm/scripts/postremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge/build/linux/nfpm/scripts/postremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge/build/linux/nfpm/scripts/preinstall.sh b/v3/examples/badge/build/linux/nfpm/scripts/preinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge/build/linux/nfpm/scripts/preinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge/build/linux/nfpm/scripts/preremove.sh b/v3/examples/badge/build/linux/nfpm/scripts/preremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/badge/build/linux/nfpm/scripts/preremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/badge/build/windows/Taskfile.yml b/v3/examples/badge/build/windows/Taskfile.yml new file mode 100644 index 000000000..534f4fb31 --- /dev/null +++ b/v3/examples/badge/build/windows/Taskfile.yml @@ -0,0 +1,63 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Windows + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - task: generate:syso + - go build {{.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 .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: windows + CGO_ENABLED: 0 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application into a `.exe` bundle + cmds: + - task: create:nsis:installer + + 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 + vars: + PRODUCTION: "true" + cmds: + # Create the Microsoft WebView2 bootstrapper if it doesn't exist + - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis" + - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}.exe' diff --git a/v3/examples/badge/build/windows/icon.ico b/v3/examples/badge/build/windows/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/badge/build/windows/icon.ico differ diff --git a/v3/examples/badge/build/windows/info.json b/v3/examples/badge/build/windows/info.json new file mode 100644 index 000000000..850b2b5b0 --- /dev/null +++ b/v3/examples/badge/build/windows/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.1.0" + }, + "info": { + "0000": { + "ProductVersion": "0.1.0", + "CompanyName": "My Company", + "FileDescription": "My Product Description", + "LegalCopyright": "© now, My Company", + "ProductName": "My Product", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/badge/build/windows/nsis/project.nsi b/v3/examples/badge/build/windows/nsis/project.nsi new file mode 100644 index 000000000..985b8e207 --- /dev/null +++ b/v3/examples/badge/build/windows/nsis/project.nsi @@ -0,0 +1,112 @@ +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 "badge" +## !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 "© now, 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 +#### +## 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. +InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). +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.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.deleteUninstaller +SectionEnd diff --git a/v3/examples/badge/build/windows/nsis/wails_tools.nsh b/v3/examples/badge/build/windows/nsis/wails_tools.nsh new file mode 100644 index 000000000..6fc10ab79 --- /dev/null +++ b/v3/examples/badge/build/windows/nsis/wails_tools.nsh @@ -0,0 +1,212 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "badge" +!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 "© now, 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 REQUEST_EXECUTION_LEVEL + !define REQUEST_EXECUTION_LEVEL "admin" +!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 + 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" +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + DeleteRegKey HKLM "${UNINST_KEY}" +!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 \ No newline at end of file diff --git a/v3/examples/badge/build/windows/wails.exe.manifest b/v3/examples/badge/build/windows/wails.exe.manifest new file mode 100644 index 000000000..fcfd2fc46 --- /dev/null +++ b/v3/examples/badge/build/windows/wails.exe.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + + + + + + + \ No newline at end of file diff --git a/v3/examples/badge/frontend/Inter Font License.txt b/v3/examples/badge/frontend/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/examples/badge/frontend/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts new file mode 100644 index 000000000..f959d50dc --- /dev/null +++ b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts @@ -0,0 +1,33 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service represents the notifications service + * @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"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * RemoveBadge removes the badge label from the application icon. + */ +export function RemoveBadge(): $CancellablePromise { + return $Call.ByID(2374916939); +} + +/** + * SetBadge sets the badge label on the application icon. + */ +export function SetBadge(label: string): $CancellablePromise { + return $Call.ByID(784276339, label); +} + +export function SetCustomBadge(label: string, options: $models.Options): $CancellablePromise { + return $Call.ByID(3058653106, label, options); +} diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts new file mode 100644 index 000000000..df3ea9723 --- /dev/null +++ b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as BadgeService from "./badgeservice.js"; +export { + BadgeService +}; + +export { + Options +} from "./models.js"; diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts new file mode 100644 index 000000000..67ed264c0 --- /dev/null +++ b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts @@ -0,0 +1,58 @@ +// 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"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as color$0 from "../../../../../../../image/color/models.js"; + +export class Options { + "TextColour": color$0.RGBA; + "BackgroundColour": color$0.RGBA; + "FontName": string; + "FontSize": number; + "SmallFontSize": number; + + /** Creates a new Options instance. */ + constructor($$source: Partial = {}) { + if (!("TextColour" in $$source)) { + this["TextColour"] = (new color$0.RGBA()); + } + if (!("BackgroundColour" in $$source)) { + this["BackgroundColour"] = (new color$0.RGBA()); + } + if (!("FontName" in $$source)) { + this["FontName"] = ""; + } + if (!("FontSize" in $$source)) { + this["FontSize"] = 0; + } + if (!("SmallFontSize" in $$source)) { + this["SmallFontSize"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Options instance from a string or object. + */ + static createFrom($$source: any = {}): Options { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("TextColour" in $$parsedSource) { + $$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]); + } + if ("BackgroundColour" in $$parsedSource) { + $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); + } + return new Options($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = color$0.RGBA.createFrom; diff --git a/v3/examples/badge/frontend/bindings/image/color/index.ts b/v3/examples/badge/frontend/bindings/image/color/index.ts new file mode 100644 index 000000000..97b507b08 --- /dev/null +++ b/v3/examples/badge/frontend/bindings/image/color/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + RGBA +} from "./models.js"; diff --git a/v3/examples/badge/frontend/bindings/image/color/models.ts b/v3/examples/badge/frontend/bindings/image/color/models.ts new file mode 100644 index 000000000..0d4eab56d --- /dev/null +++ b/v3/examples/badge/frontend/bindings/image/color/models.ts @@ -0,0 +1,46 @@ +// 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"; + +/** + * RGBA represents a traditional 32-bit alpha-premultiplied color, having 8 + * bits for each of red, green, blue and alpha. + * + * An alpha-premultiplied color component C has been scaled by alpha (A), so + * has valid values 0 <= C <= A. + */ +export class RGBA { + "R": number; + "G": number; + "B": number; + "A": number; + + /** Creates a new RGBA instance. */ + constructor($$source: Partial = {}) { + if (!("R" in $$source)) { + this["R"] = 0; + } + if (!("G" in $$source)) { + this["G"] = 0; + } + if (!("B" in $$source)) { + this["B"] = 0; + } + if (!("A" in $$source)) { + this["A"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new RGBA instance from a string or object. + */ + static createFrom($$source: any = {}): RGBA { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new RGBA($$parsedSource as Partial); + } +} diff --git a/v3/examples/badge/frontend/dist/Inter-Medium.ttf b/v3/examples/badge/frontend/dist/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/badge/frontend/dist/Inter-Medium.ttf differ diff --git a/v3/examples/badge/frontend/dist/assets/index-djydTGr9.js b/v3/examples/badge/frontend/dist/assets/index-djydTGr9.js new file mode 100644 index 000000000..130eddeb5 --- /dev/null +++ b/v3/examples/badge/frontend/dist/assets/index-djydTGr9.js @@ -0,0 +1,6 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(o){if(o.ep)return;o.ep=!0;const i=n(o);fetch(o.href,i)}})();const ce="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function $(t=21){let e="",n=t|0;for(;n--;)e+=ce[Math.random()*64|0];return e}const ae=window.location.origin+"/wails/runtime",C=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let ue=$();function z(t,e=""){return function(n,r=null){return de(t,n,e,r)}}async function de(t,e,n,r){var o,i;let s=new URL(ae);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),r&&s.searchParams.append("args",JSON.stringify(r));let c={"x-wails-client-id":ue};n&&(c["x-wails-window-name"]=n);let l=await fetch(s,{headers:c});if(!l.ok)throw new Error(await l.text());return((i=(o=l.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?l.json():l.text()}z(C.System);const P=function(){var t,e,n,r,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(r=(n=window.webkit)===null||n===void 0?void 0:n.messageHandlers)===null||r===void 0?void 0:r.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` +%c⚠️ Browser Environment Detected %c + +%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. +More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development +`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function R(t){P==null||P(t)}function Q(){return window._wails.environment.OS==="windows"}function fe(){return!!window._wails.environment.Debug}function we(){return new MouseEvent("mousedown").buttons===0}function Z(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",ge);const pe=z(C.ContextMenu),me=0;function he(t,e,n,r){pe(me,{id:t,x:e,y:n,data:r})}function ge(t){const e=Z(t),n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(n){t.preventDefault();const r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");he(n,t.clientX,t.clientY,r)}else ye(t,e)}function ye(t,e){if(fe())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const n=window.getSelection(),r=n&&n.toString().length>0;if(r)for(let o=0;o{F=t,F||(v=E=!1,u())};window.addEventListener("mousedown",N,{capture:!0});window.addEventListener("mousemove",N,{capture:!0});window.addEventListener("mouseup",N,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,be,{capture:!0});function be(t){(S||E)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const B=0,ve=1,D=2;function N(t){let e,n=t.buttons;switch(t.type){case"mousedown":e=B,H||(n=h|1<"u"||typeof e=="object"))try{var n=L.call(e);return(n===Le||n===Re||n===Te||n===ze)&&e("")==null}catch{}return!1})}function He(t){if(Y(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{y(t,null,_)}catch(e){if(e!==O)return!1}return!X(t)&&U(t)}function Be(t){if(Y(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(ke)return U(t);if(X(t))return!1;var e=L.call(t);return e!==Me&&e!==Oe&&!/^\[object HTML/.test(e)?!1:U(t)}const m=y?He:Be;var I;class W extends Error{constructor(e,n){super(e,n),this.name="CancelError"}}class x extends Error{constructor(e,n,r){super((r??"Unhandled rejection in cancelled promise.")+" Reason: "+De(n),{cause:n}),this.promise=e,this.name="CancelledRejectionError"}}const f=Symbol("barrier"),J=Symbol("cancelImpl"),V=(I=Symbol.species)!==null&&I!==void 0?I:Symbol("speciesPolyfill");class a extends Promise{constructor(e,n){let r,o;if(super((l,d)=>{r=l,o=d}),this.constructor[V]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:r,reject:o,get oncancelled(){return n??null},set oncancelled(l){n=l??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[f]:{configurable:!1,enumerable:!1,writable:!0,value:null},[J]:{configurable:!1,enumerable:!1,writable:!1,value:te(i,s)}});const c=re(i,s);try{e(ne(i,s),c)}catch(l){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",l):c(l)}}cancel(e){return new a(n=>{Promise.all([this[J](new W("Promise cancelled.",{cause:e})),Ie(this)]).then(()=>n(),()=>n())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,n,r){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(m(e)||(e=q),m(n)||(n=K),e===q&&n==K)return new a(i=>i(this));const o={};return this[f]=o,new a((i,s)=>{super.then(c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(e(c))}catch(d){s(d)}},c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(n(c))}catch(d){s(d)}})},async i=>{try{return r==null?void 0:r(i)}finally{await this.cancel(i)}})}catch(e,n){return this.then(void 0,e,n)}finally(e,n){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return m(e)?this.then(r=>a.resolve(e()).then(()=>r),r=>a.resolve(e()).then(()=>{throw r}),n):this.then(e,e,n)}static get[V](){return Promise}static all(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.all(n).then(o,i)},o=>M(r,n,o));return r}static allSettled(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.allSettled(n).then(o,i)},o=>M(r,n,o));return r}static any(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.any(n).then(o,i)},o=>M(r,n,o));return r}static race(e){let n=Array.from(e);const r=new a((o,i)=>{Promise.race(n).then(o,i)},o=>M(r,n,o));return r}static cancel(e){const n=new a(()=>{});return n.cancel(e),n}static timeout(e,n){const r=new a(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void r.cancel(n)):setTimeout(()=>void r.cancel(n),e),r}static sleep(e,n){return new a(r=>{setTimeout(()=>r(n),e)})}static reject(e){return new a((n,r)=>r(e))}static resolve(e){return e instanceof a?e:new a(n=>n(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new a((n,r)=>{e.resolve=n,e.reject=r},n=>{var r;(r=e.oncancelled)===null||r===void 0||r.call(e,n)}),e}}function te(t,e){let n;return r=>{if(e.settled||(e.settled=!0,e.reason=r,t.reject(r),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==r)throw o})),!(!e.reason||!t.oncancelled))return n=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new x(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new x(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,n}}function ne(t,e){return n=>{if(!e.resolving){if(e.resolving=!0,n===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(n!=null&&(typeof n=="object"||typeof n=="function")){let r;try{r=n.then}catch(o){e.settled=!0,t.reject(o);return}if(m(r)){try{let s=n.cancel;if(m(s)){const c=l=>{Reflect.apply(s,n,[l])};e.reason?te(Object.assign(Object.assign({},t),{oncancelled:c}),e)(e.reason):t.oncancelled=c}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=re(t,o);try{Reflect.apply(r,n,[ne(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(n))}}}function re(t,e){return n=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(n instanceof W&&e.reason instanceof W&&Object.is(n.cause,e.reason.cause))return}catch{}Promise.reject(new x(t.promise,n))}else e.settled=!0,t.reject(n)}}function M(t,e,n){const r=[];for(const o of e){let i;try{if(!m(o.then)||(i=o.cancel,!m(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[n])}catch(c){Promise.reject(new x(t,c,"Unhandled exception in cancel method."));continue}s&&r.push((s instanceof Promise?s:Promise.resolve(s)).catch(c=>{Promise.reject(new x(t,c,"Unhandled rejection in cancel method."))}))}return Promise.all(r)}function q(t){return t}function K(t){throw t}function De(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function Ie(t){var e;let n=(e=t[f])!==null&&e!==void 0?e:{};return"promise"in n||Object.assign(n,g()),t[f]==null&&(n.resolve(),t[f]=n),n.promise}let g=Promise.withResolvers;g&&typeof g=="function"?g=g.bind(Promise):g=function(){let t,e;return{promise:new Promise((r,o)=>{t=r,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Xe;window._wails.callErrorHandler=Ye;const Fe=z(C.Call),_e=z(C.CancelCall),b=new Map,Ue=0,We=0;class Ne extends Error{constructor(e,n){super(e,n),this.name="RuntimeError"}}function Xe(t,e,n){const r=oe(t);if(r)if(!e)r.resolve(void 0);else if(!n)r.resolve(e);else try{r.resolve(JSON.parse(e))}catch(o){r.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function Ye(t,e,n){const r=oe(t);if(r)if(!n)r.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(c){r.reject(new TypeError("could not parse error: "+c.message,{cause:c}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new Ne(o.message,i);break;default:s=new Error(o.message,i);break}r.reject(s)}}function oe(t){const e=b.get(t);return b.delete(t),e}function Ge(){let t;do t=$();while(b.has(t));return t}function Je(t){const e=Ge(),n=a.withResolvers();b.set(e,{resolve:n.resolve,reject:n.reject});const r=Fe(Ue,Object.assign({"call-id":e},t));let o=!1;r.then(()=>{o=!0},s=>{b.delete(e),n.reject(s)});const i=()=>(b.delete(e),_e(We,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return n.oncancelled=()=>o?i():r.then(i),n.promise}function ie(t,...e){return Je({methodID:t,args:e})}const w=new Map;class Ve{constructor(e,n,r){this.eventName=e,this.callback=n,this.maxCallbacks=r||-1}dispatch(e){try{this.callback(e)}catch(n){console.error(n)}return this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0)}}function qe(t){let e=w.get(t.eventName);e&&(e=e.filter(n=>n!==t),e.length===0?w.delete(t.eventName):w.set(t.eventName,e))}window._wails=window._wails||{};window._wails.dispatchWailsEvent=Ze;const Ke=z(C.Events),$e=0;class Qe{constructor(e,n=null){this.name=e,this.data=n}}function Ze(t){let e=w.get(t.name);if(!e)return;let n=new Qe(t.name,t.data);"sender"in t&&(n.sender=t.sender),e=e.filter(r=>!r.dispatch(n)),e.length===0?w.delete(t.name):w.set(t.name,e)}function et(t,e,n){let r=w.get(t)||[];const o=new Ve(t,e,n);return r.push(o),w.set(t,r),()=>qe(o)}function tt(t,e){return et(t,e,-1)}function se(t){return Ke($e,t)}window._wails=window._wails||{};window._wails.invoke=R;R("wails:runtime:ready");function nt(){return ie(2374916939)}function rt(t){return ie(784276339,t)}const ot=document.getElementById("set"),it=document.getElementById("remove"),st=document.getElementById("set-go"),lt=document.getElementById("remove-go"),le=document.getElementById("label"),ct=document.getElementById("time");ot.addEventListener("click",()=>{let t=le.value;rt(t)});it.addEventListener("click",()=>{nt()});st.addEventListener("click",()=>{let t=le.value;se({name:"set:badge",data:t})});lt.addEventListener("click",()=>{se({name:"remove:badge",data:null})});tt("time",t=>{ct.innerText=t.data}); diff --git a/v3/examples/badge/frontend/dist/index.html b/v3/examples/badge/frontend/dist/index.html new file mode 100644 index 000000000..d5b44d109 --- /dev/null +++ b/v3/examples/badge/frontend/dist/index.html @@ -0,0 +1,38 @@ + + + + + + + + Wails App + + + +
+ +

Wails + Typescript

+
Set a badge label below 👇
+
+
+ + + + + +
+
+ +
+ + diff --git a/v3/examples/badge/frontend/dist/style.css b/v3/examples/badge/frontend/dist/style.css new file mode 100644 index 000000000..6ce81cad2 --- /dev/null +++ b/v3/examples/badge/frontend/dist/style.css @@ -0,0 +1,155 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/badge/frontend/dist/typescript.svg b/v3/examples/badge/frontend/dist/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/badge/frontend/dist/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/badge/frontend/dist/wails.png b/v3/examples/badge/frontend/dist/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/badge/frontend/dist/wails.png differ diff --git a/v3/examples/badge/frontend/index.html b/v3/examples/badge/frontend/index.html new file mode 100644 index 000000000..616cb4c0f --- /dev/null +++ b/v3/examples/badge/frontend/index.html @@ -0,0 +1,38 @@ + + + + + + + + Wails App + + +
+ +

Wails + Typescript

+
Set a badge label below 👇
+
+
+ + + + + +
+
+ +
+ + + diff --git a/v3/examples/badge/frontend/package.json b/v3/examples/badge/frontend/package.json new file mode 100644 index 000000000..b39da7ece --- /dev/null +++ b/v3/examples/badge/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^5.0.0" + } +} diff --git a/v3/examples/badge/frontend/public/Inter-Medium.ttf b/v3/examples/badge/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/badge/frontend/public/Inter-Medium.ttf differ diff --git a/v3/examples/badge/frontend/public/style.css b/v3/examples/badge/frontend/public/style.css new file mode 100644 index 000000000..6ce81cad2 --- /dev/null +++ b/v3/examples/badge/frontend/public/style.css @@ -0,0 +1,155 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/badge/frontend/public/typescript.svg b/v3/examples/badge/frontend/public/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/badge/frontend/public/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/badge/frontend/public/wails.png b/v3/examples/badge/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/badge/frontend/public/wails.png differ diff --git a/v3/examples/badge/frontend/src/main.ts b/v3/examples/badge/frontend/src/main.ts new file mode 100644 index 000000000..934741aba --- /dev/null +++ b/v3/examples/badge/frontend/src/main.ts @@ -0,0 +1,35 @@ +import {Events} from "@wailsio/runtime"; +import {SetBadge, RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice"; + +const setButton = document.getElementById('set')! as HTMLButtonElement; +const removeButton = document.getElementById('remove')! as HTMLButtonElement; +const setButtonUsingGo = document.getElementById('set-go')! as HTMLButtonElement; +const removeButtonUsingGo = document.getElementById('remove-go')! as HTMLButtonElement; +const labelElement : HTMLInputElement = document.getElementById('label')! as HTMLInputElement; +const timeElement = document.getElementById('time')! as HTMLDivElement; + +setButton.addEventListener('click', () => { + let label = (labelElement as HTMLInputElement).value + SetBadge(label); +}); + +removeButton.addEventListener('click', () => { + RemoveBadge(); +}); + +setButtonUsingGo.addEventListener('click', () => { + let label = (labelElement as HTMLInputElement).value + void Events.Emit({ + name: "set:badge", + data: label, + }) +}) + +removeButtonUsingGo.addEventListener('click', () => { + void Events.Emit({name:"remove:badge", data: null}) +}) + +Events.On('time', (time: {data: any}) => { + timeElement.innerText = time.data; +}); + diff --git a/v3/examples/badge/frontend/src/vite-env.d.ts b/v3/examples/badge/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/examples/badge/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/examples/badge/frontend/tsconfig.json b/v3/examples/badge/frontend/tsconfig.json new file mode 100644 index 000000000..c267ecf24 --- /dev/null +++ b/v3/examples/badge/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/v3/examples/badge/main.go b/v3/examples/badge/main.go new file mode 100644 index 000000000..9de2944c0 --- /dev/null +++ b/v3/examples/badge/main.go @@ -0,0 +1,109 @@ +package main + +import ( + "embed" + _ "embed" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/services/badge" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed all:frontend/dist +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + + badgeService := badge.New() + + app := application.New(application.Options{ + Name: "badge", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(badgeService), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + // Store cleanup functions for proper resource management + removeBadgeHandler := app.Event.On("remove:badge", func(event *application.CustomEvent) { + err := badgeService.RemoveBadge() + if err != nil { + log.Fatal(err) + } + }) + + setBadgeHandler := app.Event.On("set:badge", func(event *application.CustomEvent) { + text := event.Data.(string) + err := badgeService.SetBadge(text) + if err != nil { + log.Fatal(err) + } + }) + + // Note: In a production application, you would call these cleanup functions + // when the handlers are no longer needed, e.g., during shutdown: + // defer removeBadgeHandler() + // defer setBadgeHandler() + _ = removeBadgeHandler // Acknowledge we're storing the cleanup functions + _ = setBadgeHandler + + // Create a goroutine that emits an event containing the current time every second. + // The frontend can listen to this event and update the UI accordingly. + go func() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + now := time.Now().Format(time.RFC1123) + app.Event.Emit("time", now) + case <-app.Context().Done(): + return + } + } + }() + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/binding/GreetService.go b/v3/examples/binding/GreetService.go new file mode 100644 index 000000000..262fea722 --- /dev/null +++ b/v3/examples/binding/GreetService.go @@ -0,0 +1,39 @@ +package main + +import ( + "strconv" + + "github.com/wailsapp/wails/v3/examples/binding/data" +) + +// GreetService is a service that greets people +type GreetService struct { +} + +// Greet greets a person +func (*GreetService) Greet(name string, counts ...int) string { + times := " " + + for index, count := range counts { + if index > 0 { + times += ", " + } + times += strconv.Itoa(count) + } + + if len(counts) > 0 { + times += " times " + } + + return "Hello" + times + name +} + +// GreetPerson greets a person +func (srv *GreetService) GreetPerson(person data.Person) string { + return srv.Greet(person.Name, person.Counts...) +} + +// GetPerson returns a person with the given name. +func (srv *GreetService) GetPerson(name string) data.Person { + return data.Person{Name: name} +} diff --git a/v3/examples/binding/README.md b/v3/examples/binding/README.md new file mode 100644 index 000000000..37d0f2cc8 --- /dev/null +++ b/v3/examples/binding/README.md @@ -0,0 +1,11 @@ +# Bindings Example + +This example demonstrates how to generate bindings for your application. + +To generate bindings, run `wails3 generate bindings -clean -b -d assets/bindings` in this directory. + +See more options by running `wails3 generate bindings --help`. + +## Notes + - The bindings generator is still a work in progress and is subject to change. + - The generated code uses `wails.CallByID` by default. This is the most robust way to call a function. diff --git a/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/data/index.js b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/data/index.js new file mode 100644 index 000000000..4c7e0b9c6 --- /dev/null +++ b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/data/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Person +} from "./models.js"; diff --git a/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/data/models.js b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/data/models.js new file mode 100644 index 000000000..19a39472c --- /dev/null +++ b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/data/models.js @@ -0,0 +1,55 @@ +// @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 "/wails/runtime.js"; + +/** + * Person holds someone's most important attributes + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("name" in $$source)) { + /** + * Name is the person's name + * @member + * @type {string} + */ + this["name"] = ""; + } + if (!("counts" in $$source)) { + /** + * Counts tracks the number of time the person + * has been greeted in various ways + * @member + * @type {number[]} + */ + this["counts"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("counts" in $$parsedSource) { + $$parsedSource["counts"] = $$createField1_0($$parsedSource["counts"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); diff --git a/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/greetservice.js b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/greetservice.js new file mode 100644 index 000000000..0e5e40d84 --- /dev/null +++ b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/greetservice.js @@ -0,0 +1,49 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is a service that greets people + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as data$0 from "./data/models.js"; + +/** + * GetPerson returns a person with the given name. + * @param {string} name + * @returns {$CancellablePromise} + */ +export function GetPerson(name) { + return $Call.ByID(2952413357, name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +/** + * Greet greets a person + * @param {string} name + * @param {number[]} counts + * @returns {$CancellablePromise} + */ +export function Greet(name, ...counts) { + return $Call.ByID(1411160069, name, counts); +} + +/** + * GreetPerson greets a person + * @param {data$0.Person} person + * @returns {$CancellablePromise} + */ +export function GreetPerson(person) { + return $Call.ByID(4021313248, person); +} + +// Private type creation functions +const $$createType0 = data$0.Person.createFrom; diff --git a/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/index.js b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/examples/binding/assets/bindings/github.com/wailsapp/wails/v3/examples/binding/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/examples/binding/assets/index.html b/v3/examples/binding/assets/index.html new file mode 100644 index 000000000..c86c7b7af --- /dev/null +++ b/v3/examples/binding/assets/index.html @@ -0,0 +1,130 @@ + + + + + Wails Alpha + + + + +
Alpha
+
+

Documentation

+

Feedback

+
+
Please enter your name below 👇
+
+ + + +
+ + + diff --git a/v3/examples/binding/data/person.go b/v3/examples/binding/data/person.go new file mode 100644 index 000000000..114dfdf4c --- /dev/null +++ b/v3/examples/binding/data/person.go @@ -0,0 +1,11 @@ +package data + +// Person holds someone's most important attributes +type Person struct { + // Name is the person's name + Name string `json:"name"` + + // Counts tracks the number of time the person + // has been greeted in various ways + Counts []int `json:"counts"` +} diff --git a/v3/examples/binding/main.go b/v3/examples/binding/main.go new file mode 100644 index 000000000..6e956bac1 --- /dev/null +++ b/v3/examples/binding/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + URL: "/", + DevToolsEnabled: true, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/build/README.md b/v3/examples/build/README.md new file mode 100644 index 000000000..3e2c246a0 --- /dev/null +++ b/v3/examples/build/README.md @@ -0,0 +1,23 @@ +# Build + +Wails has adopted [Taskfile](https://taskfile.dev) as its build tool. This is optional +and any build tool can be used. However, Taskfile is a great tool, and we recommend it. + +The Wails CLI has built-in integration with Taskfile so the standalone version is not a +requirement. + +## Building + +To build the example, run: + +```bash +wails3 task build +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | \ No newline at end of file diff --git a/v3/examples/build/Taskfile.yml b/v3/examples/build/Taskfile.yml new file mode 100644 index 000000000..f4e5e1f15 --- /dev/null +++ b/v3/examples/build/Taskfile.yml @@ -0,0 +1,110 @@ +version: '3' + +vars: + APP_NAME: "buildtest{{exeExt}}" +tasks: + + build: + summary: Builds the application + cmds: + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + + package: + summary: Packages a production build of the application into a `.app` or `.exe` bundle + deps: + - task: build + cmds: + - task: package:darwin + - task: package:windows + +# ------------------------------------------------------------------------------ + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + cmds: + # Generates both .ico and .icns files + - wails3 generate icons -input appicon.png + + build:production:darwin: + summary: Creates a production build of the application + cmds: + - GOOS=darwin GOARCH={{.GOARCH}} go build -tags production -ldflags="-w -s" -o build/bin/{{.APP_NAME}} + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" + GOARCH: '{{.GOARCH}}' + + build:production:linux: + summary: Creates a production build of the application + cmds: + - GOOS=linux GOARCH={{.GOARCH}} go build -tags production -ldflags="-w -s" -o build/bin/{{.APP_NAME}} + env: + GOARCH: '{{.GOARCH}}' + + generate:app_bundle: + summary: Builds a `.app` bundle + cmds: + - mkdir -p {{.APP_NAME}}.app/Contents/{MacOS,Resources} + - cp build/icons.icns {{.APP_NAME}}.app/Contents/Resources + - cp build/bin/{{.APP_NAME}} {{.APP_NAME}}.app/Contents/MacOS + - cp build/Info.plist {{.APP_NAME}}.app/Contents + + package:darwin:arm64: + summary: Packages a production build of the application into a `.app` bundle + platforms: + - darwin + deps: + - task: build:production:darwin + vars: + ARCH: arm64 + - generate:icons + cmds: + - task: generate:app_bundle + + package:darwin: + summary: Packages a production build of the application into a `.app` bundle + platforms: + - darwin + deps: + - task: build:production:darwin + - generate:icons + cmds: + - task: generate:app_bundle + + generate:syso: + dir: build + platforms: + - windows + cmds: + - wails3 generate syso -arch {{.GOARCH}} -icon icon.ico -manifest wails.exe.manifest -info info.json -out ../wails_windows.syso + vars: + GOARCH: '{{.GOARCH}}' + + package:windows: + summary: Packages a production build of the application into a `.exe` bundle + platforms: + - windows/amd64 + deps: + - generate:icons + cmds: + - task: generate:syso + vars: + GOARCH: amd64 + - go build -tags production -ldflags="-w -s -H windowsgui" -o bin/{{.APP_NAME}} + - powershell Remove-item *.syso + + + package:windows:arm64: + summary: Packages a production build of the application into a `.exe` bundle (ARM64) + platforms: + - windows + cmds: + - task: generate:syso + vars: + GOARCH: arm64 + - go build -tags production -ldflags="-w -s -H windowsgui" -o bin/buildtest.arm64.exe + - powershell Remove-item *.syso + env: + GOARCH: arm64 diff --git a/v3/examples/build/build/Info.dev.plist b/v3/examples/build/build/Info.dev.plist new file mode 100644 index 000000000..d6d28b179 --- /dev/null +++ b/v3/examples/build/build/Info.dev.plist @@ -0,0 +1,35 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My App + CFBundleExecutable + app + CFBundleIdentifier + com.wails.app + CFBundleVersion + v1.0.0 + CFBundleGetInfoString + The ultimate thing + CFBundleShortVersionString + v1 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) Me + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + + + + \ No newline at end of file diff --git a/v3/examples/build/build/Info.plist b/v3/examples/build/build/Info.plist new file mode 100644 index 000000000..7f03f54e9 --- /dev/null +++ b/v3/examples/build/build/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My App + CFBundleExecutable + app + CFBundleIdentifier + com.wails.app + CFBundleVersion + v1.0.0 + CFBundleGetInfoString + The ultimate thing + CFBundleShortVersionString + v1 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) Me + + \ No newline at end of file diff --git a/v3/examples/build/build/appicon.png b/v3/examples/build/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/build/build/appicon.png differ diff --git a/v3/examples/build/build/icon.ico b/v3/examples/build/build/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/build/build/icon.ico differ diff --git a/v3/examples/build/build/icons.icns b/v3/examples/build/build/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/build/build/icons.icns differ diff --git a/v3/examples/build/build/info.json b/v3/examples/build/build/info.json new file mode 100644 index 000000000..1005eb5cb --- /dev/null +++ b/v3/examples/build/build/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "v1.0.0" + }, + "info": { + "0000": { + "ProductVersion": "v1.0.0", + "CompanyName": "My Company Name", + "FileDescription": "A thing that does a thing", + "LegalCopyright": "(c) 2023 My Company Name", + "ProductName": "My Product Name", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/build/build/wails.exe.manifest b/v3/examples/build/build/wails.exe.manifest new file mode 100644 index 000000000..fb1ce5bde --- /dev/null +++ b/v3/examples/build/build/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v3/examples/build/main.go b/v3/examples/build/main.go new file mode 100755 index 000000000..3098527b7 --- /dev/null +++ b/v3/examples/build/main.go @@ -0,0 +1,273 @@ +package main + +import ( + _ "embed" + "fmt" + "log" + "math/rand" + "runtime" + "strconv" + "time" + + "github.com/wailsapp/wails/v3/pkg/events" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + + app := application.New(application.Options{ + Name: "WebviewWindow Demo (debug)", + Description: "A demo of the WebviewWindow API", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Event.OnApplicationEvent(events.Mac.ApplicationDidFinishLaunching, func(*application.ApplicationEvent) { + log.Println("ApplicationDidFinishLaunching") + }) + + currentWindow := func(fn func(window application.Window)) { + current := app.Window.Current() + if current != nil { + fn(current) + } else { + println("Current WebviewWindow is nil") + } + } + + // Create a custom menu + menu := app.NewMenu() + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + + windowCounter := 1 + + // Let's make a "Demo" menu + myMenu := menu.AddSubmenu("New") + + myMenu.Add("New WebviewWindow"). + SetAccelerator("CmdOrCtrl+N"). + OnClick(func(ctx *application.Context) { + app.Window.New(). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New Frameless WebviewWindow"). + SetAccelerator("CmdOrCtrl+F"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + X: rand.Intn(1000), + Y: rand.Intn(800), + Frameless: true, + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + }, + }).Show() + windowCounter++ + }) + if runtime.GOOS == "darwin" { + myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHiddenInset, + InvisibleTitleBarHeight: 25, + }, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetHTML("

A MacTitleBarHiddenInset WebviewWindow example

"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetHTML("

A MacTitleBarHiddenInsetUnified WebviewWindow example

"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (MacTitleBarHidden)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHidden, + InvisibleTitleBarHeight: 25, + }, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetHTML("

A MacTitleBarHidden WebviewWindow example

"). + Show() + windowCounter++ + }) + } + + sizeMenu := menu.AddSubmenu("Size") + sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetSize(800, 600) + }) + }) + + sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200) + }) + }) + sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinSize(200, 200) + }) + }) + sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaximiseButtonState(application.ButtonDisabled) + w.SetMaxSize(600, 600) + }) + }) + sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + width, height := w.Size() + application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show() + }) + }) + + sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinSize(0, 0) + }) + }) + + sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaxSize(0, 0) + w.SetMaximiseButtonState(application.ButtonEnabled) + }) + }) + positionMenu := menu.AddSubmenu("Position") + positionMenu.Add("Set Relative Position (0,0)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetRelativePosition(0, 0) + }) + }) + positionMenu.Add("Set Relative Position (Random)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetRelativePosition(rand.Intn(1000), rand.Intn(800)) + }) + }) + + positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + x, y := w.RelativePosition() + application.InfoDialog().SetTitle("Current WebviewWindow Relative Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() + }) + }) + + positionMenu.Add("Center").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Center() + }) + }) + stateMenu := menu.AddSubmenu("State") + stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Minimise() + time.Sleep(2 * time.Second) + w.Restore() + }) + }) + stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Maximise() + }) + }) + stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Fullscreen() + }) + }) + stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.UnFullscreen() + }) + }) + stateMenu.Add("Restore").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Restore() + }) + }) + stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Hide() + time.Sleep(2 * time.Second) + w.Show() + }) + }) + stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetAlwaysOnTop(true) + }) + }) + stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetAlwaysOnTop(false) + }) + }) + stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetURL("https://google.com") + }) + }) + stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetURL("https://wails.io") + }) + }) + stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) { + screen := app.Screen.GetPrimary() + msg := fmt.Sprintf("Screen: %+v", screen) + application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show() + }) + stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) { + screens := app.Screen.GetAll() + for _, screen := range screens { + msg := fmt.Sprintf("Screen: %+v", screen) + application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show() + } + }) + stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + screen, err := w.GetScreen() + if err != nil { + application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show() + return + } + msg := fmt.Sprintf("Screen: %+v", screen) + application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show() + }) + }) + app.Window.New() + + app.Menu.Set(menu) + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/cancel-async/README.md b/v3/examples/cancel-async/README.md new file mode 100644 index 000000000..feec0e5d6 --- /dev/null +++ b/v3/examples/cancel-async/README.md @@ -0,0 +1,5 @@ +# Binding Call Cancelling Example + +This example demonstrates how to cancel binding calls in async/await style. + +To regenerate bindings, run `wails3 generate bindings -clean -b -d assets/bindings` in this directory. diff --git a/v3/examples/cancel-async/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-async/index.js b/v3/examples/cancel-async/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-async/index.js new file mode 100644 index 000000000..d2bf43c94 --- /dev/null +++ b/v3/examples/cancel-async/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-async/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 Service from "./service.js"; +export { + Service +}; diff --git a/v3/examples/cancel-async/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-async/service.js b/v3/examples/cancel-async/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-async/service.js new file mode 100644 index 000000000..6aae91e1d --- /dev/null +++ b/v3/examples/cancel-async/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-async/service.js @@ -0,0 +1,16 @@ +// @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 {Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create} from "/wails/runtime.js"; + +/** + * A long running operation of specified duration. + * @param {number} milliseconds + * @returns {$CancellablePromise} + */ +export function LongRunning(milliseconds) { + return $Call.ByID(298191698, milliseconds); +} diff --git a/v3/examples/cancel-async/assets/index.html b/v3/examples/cancel-async/assets/index.html new file mode 100644 index 000000000..ff0609928 --- /dev/null +++ b/v3/examples/cancel-async/assets/index.html @@ -0,0 +1,134 @@ + + + + + Wails Alpha + + + + +
Alpha
+
+

Documentation

+

Feedback

+
+
Please enter a duration in milliseconds below 👇
+
+ + + + +
+ + + diff --git a/v3/examples/cancel-async/main.go b/v3/examples/cancel-async/main.go new file mode 100644 index 000000000..9fb52481b --- /dev/null +++ b/v3/examples/cancel-async/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&Service{}), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + URL: "/", + DevToolsEnabled: true, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/cancel-async/service.go b/v3/examples/cancel-async/service.go new file mode 100644 index 000000000..b2cc72e9b --- /dev/null +++ b/v3/examples/cancel-async/service.go @@ -0,0 +1,19 @@ +package main + +import ( + "context" + "time" +) + +type Service struct { +} + +// A long running operation of specified duration. +func (*Service) LongRunning(ctx context.Context, milliseconds int) error { + select { + case <-time.After(time.Duration(milliseconds) * time.Millisecond): + return nil + case <-ctx.Done(): + return ctx.Err() + } +} diff --git a/v3/examples/cancel-chaining/README.md b/v3/examples/cancel-chaining/README.md new file mode 100644 index 000000000..10c5855ea --- /dev/null +++ b/v3/examples/cancel-chaining/README.md @@ -0,0 +1,5 @@ +# Binding Call Cancelling Example + +This example demonstrates how to cancel binding calls in promise chaining style. + +To regenerate bindings, run `wails3 generate bindings -clean -b -d assets/bindings` in this directory. diff --git a/v3/examples/cancel-chaining/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-chaining/index.js b/v3/examples/cancel-chaining/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-chaining/index.js new file mode 100644 index 000000000..d2bf43c94 --- /dev/null +++ b/v3/examples/cancel-chaining/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-chaining/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 Service from "./service.js"; +export { + Service +}; diff --git a/v3/examples/cancel-chaining/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-chaining/service.js b/v3/examples/cancel-chaining/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-chaining/service.js new file mode 100644 index 000000000..6aae91e1d --- /dev/null +++ b/v3/examples/cancel-chaining/assets/bindings/github.com/wailsapp/wails/v3/examples/cancel-chaining/service.js @@ -0,0 +1,16 @@ +// @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 {Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create} from "/wails/runtime.js"; + +/** + * A long running operation of specified duration. + * @param {number} milliseconds + * @returns {$CancellablePromise} + */ +export function LongRunning(milliseconds) { + return $Call.ByID(298191698, milliseconds); +} diff --git a/v3/examples/cancel-chaining/assets/index.html b/v3/examples/cancel-chaining/assets/index.html new file mode 100644 index 000000000..71221c28d --- /dev/null +++ b/v3/examples/cancel-chaining/assets/index.html @@ -0,0 +1,136 @@ + + + + + Wails Alpha + + + + +
Alpha
+
+

Documentation

+

Feedback

+
+
Please enter a duration in milliseconds below 👇
+
+ + + + +
+ + + diff --git a/v3/examples/cancel-chaining/main.go b/v3/examples/cancel-chaining/main.go new file mode 100644 index 000000000..9fb52481b --- /dev/null +++ b/v3/examples/cancel-chaining/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&Service{}), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + URL: "/", + DevToolsEnabled: true, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/cancel-chaining/service.go b/v3/examples/cancel-chaining/service.go new file mode 100644 index 000000000..b2cc72e9b --- /dev/null +++ b/v3/examples/cancel-chaining/service.go @@ -0,0 +1,19 @@ +package main + +import ( + "context" + "time" +) + +type Service struct { +} + +// A long running operation of specified duration. +func (*Service) LongRunning(ctx context.Context, milliseconds int) error { + select { + case <-time.After(time.Duration(milliseconds) * time.Millisecond): + return nil + case <-ctx.Done(): + return ctx.Err() + } +} diff --git a/v3/examples/clipboard/README.md b/v3/examples/clipboard/README.md new file mode 100644 index 000000000..b3128b531 --- /dev/null +++ b/v3/examples/clipboard/README.md @@ -0,0 +1,11 @@ +# clipboard + +This example demonstrates how to use the clipboard API. + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | \ No newline at end of file diff --git a/v3/examples/clipboard/main.go b/v3/examples/clipboard/main.go new file mode 100644 index 000000000..efeed80de --- /dev/null +++ b/v3/examples/clipboard/main.go @@ -0,0 +1,77 @@ +package main + +import ( + _ "embed" + "log" + "runtime" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + + app := application.New(application.Options{ + Name: "Clipboard Demo", + Description: "A demo of the clipboard API", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a custom menu + menu := app.NewMenu() + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + + setClipboardMenu := menu.AddSubmenu("Set Clipboard") + setClipboardMenu.Add("Set Text 'Hello'").OnClick(func(ctx *application.Context) { + success := app.Clipboard.SetText("Hello") + if !success { + application.InfoDialog().SetMessage("Failed to set clipboard text").Show() + } + }) + setClipboardMenu.Add("Set Text 'World'").OnClick(func(ctx *application.Context) { + success := app.Clipboard.SetText("World") + if !success { + application.InfoDialog().SetMessage("Failed to set clipboard text").Show() + } + }) + setClipboardMenu.Add("Set Text (current time)").OnClick(func(ctx *application.Context) { + success := app.Clipboard.SetText(time.Now().String()) + if !success { + application.InfoDialog().SetMessage("Failed to set clipboard text").Show() + } + }) + getClipboardMenu := menu.AddSubmenu("Get Clipboard") + getClipboardMenu.Add("Get Text").OnClick(func(ctx *application.Context) { + result, ok := app.Clipboard.Text() + if !ok { + application.InfoDialog().SetMessage("Failed to get clipboard text").Show() + } else { + application.InfoDialog().SetMessage("Got:\n\n" + result).Show() + } + }) + + clearClipboardMenu := menu.AddSubmenu("Clear Clipboard") + clearClipboardMenu.Add("Clear Text").OnClick(func(ctx *application.Context) { + success := app.Clipboard.SetText("") + if success { + application.InfoDialog().SetMessage("Clipboard text cleared").Show() + } else { + application.InfoDialog().SetMessage("Clipboard text not cleared").Show() + } + }) + + app.Menu.Set(menu) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/contextmenus/README.md b/v3/examples/contextmenus/README.md new file mode 100644 index 000000000..ba6d498e7 --- /dev/null +++ b/v3/examples/contextmenus/README.md @@ -0,0 +1,30 @@ +# contextmenus + +This example shows how to create a context menu for your application. +It demonstrates window level and global context menus. + +A simple menu is registered with the window and the application with the id "test". +In our frontend html, we then use the `--custom-contextmenu` style to attach the menu to an element. +We also use the `--custom-contextmenu-data` style to pass data to the menu callback which can be read in Go. +This is really useful when using components to distinguish between different elements. + +```go + +```html + +
+

1

+
+
+

2

+
+``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | + diff --git a/v3/examples/contextmenus/assets/index.html b/v3/examples/contextmenus/assets/index.html new file mode 100644 index 000000000..23224084d --- /dev/null +++ b/v3/examples/contextmenus/assets/index.html @@ -0,0 +1,82 @@ + + + + + Title + + + + + + +

Context Menu Demo

+

+ You need to run this example in production for it to work : +

go run -tags production .
+ +
+
+

1

+
+
+

2

+
+
+

Default Context Menu shown here

+ +
+
+

Default auto (smart) Context Menu here

+ +
+
+

Context menu shown here only if you select text

+

Selecting text here and right-clicking the box below or its border shouldn't show the context menu

+
+
+
+
+
+
+ + + + +

content editable

+
+
+
+

Default Context Menu hidden here

+

Context Menu hidden here even if text is selected

+ +
+
+

Nested section reverted to auto (smart) default Context Menu

+

Context menu shown here only if you select text

+
+
+
+ + diff --git a/v3/examples/contextmenus/main.go b/v3/examples/contextmenus/main.go new file mode 100644 index 000000000..70cdf5c7e --- /dev/null +++ b/v3/examples/contextmenus/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Context Menu Demo", + Description: "A demo of the Context Menu API", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Context Menu Demo", + Width: 1024, + Height: 1024, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + contextMenu := app.ContextMenu.New() + clickMe := contextMenu.Add("Click to set Menuitem label to Context Data") + contextDataMenuItem := contextMenu.Add("Current context data: No Context Data") + clickMe.OnClick(func(data *application.Context) { + app.Logger.Info("Context menu", "context data", data.ContextMenuData()) + contextDataMenuItem.SetLabel("Current context data: " + data.ContextMenuData()) + contextMenu.Update() + }) + + // Register the context menu + app.ContextMenu.Add("test", contextMenu) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/custom-protocol-example/.gitignore b/v3/examples/custom-protocol-example/.gitignore new file mode 100644 index 000000000..ba8194ab6 --- /dev/null +++ b/v3/examples/custom-protocol-example/.gitignore @@ -0,0 +1,6 @@ +.task +bin +frontend/dist +frontend/node_modules +build/linux/appimage/build +build/windows/nsis/MicrosoftEdgeWebview2Setup.exe \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/README.md b/v3/examples/custom-protocol-example/README.md new file mode 100644 index 000000000..ad12c3f40 --- /dev/null +++ b/v3/examples/custom-protocol-example/README.md @@ -0,0 +1,59 @@ +# Welcome to Your New Wails3 Project! + +Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running. + +## Getting Started + +1. Navigate to your project directory in the terminal. + +2. To run your application in development mode, use the following command: + + ``` + wails3 dev + ``` + + This will start your application and enable hot-reloading for both frontend and backend changes. + +3. To build your application for production, use: + + ``` + wails3 build + ``` + + This will create a production-ready executable in the `build` directory. + +## Exploring Wails3 Features + +Now that you have your project set up, it's time to explore the features that Wails3 offers: + +1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications. + +2. **Run an example**: To run any of the examples, navigate to the example's directory and use: + + ``` + go run . + ``` + + Note: Some examples may be under development during the alpha phase. + +3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3.wails.io/) for in-depth guides and API references. + +4. **Join the community**: Have questions or want to share your progress? Join the [Wails Discord](https://discord.gg/JDdSxwjhGf) or visit the [Wails discussions on GitHub](https://github.com/wailsapp/wails/discussions). + +## Project Structure + +Take a moment to familiarize yourself with your project structure: + +- `frontend/`: Contains your frontend code (HTML, CSS, JavaScript/TypeScript) +- `main.go`: The entry point of your Go backend +- `app.go`: Define your application structure and methods here +- `wails.json`: Configuration file for your Wails project + +## Next Steps + +1. Modify the frontend in the `frontend/` directory to create your desired UI. +2. Add backend functionality in `main.go`. +3. Use `wails3 dev` to see your changes in real-time. +4. When ready, build your application with `wails3 build`. + +Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community. diff --git a/v3/examples/custom-protocol-example/Taskfile.yml b/v3/examples/custom-protocol-example/Taskfile.yml new file mode 100644 index 000000000..572db59ac --- /dev/null +++ b/v3/examples/custom-protocol-example/Taskfile.yml @@ -0,0 +1,33 @@ +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "custom-protocol-example" + BIN_DIR: "bin" + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml + diff --git a/v3/examples/custom-protocol-example/build/Taskfile.yml b/v3/examples/custom-protocol-example/build/Taskfile.yml new file mode 100644 index 000000000..ba497b5b6 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/Taskfile.yml @@ -0,0 +1,85 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + dir: frontend + sources: + - package.json + - package-lock.json + generates: + - node_modules/* + preconditions: + - sh: npm version + cmds: + - echo "Skipping frontend dependencies installation" + + build:frontend: + label: build:frontend (PRODUCTION={{.PRODUCTION}}) + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/**/* + deps: + - task: install:frontend:deps + - task: generate:bindings + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + cmds: + - npm run {{.BUILD_COMMAND}} -q + env: + PRODUCTION: '{{.PRODUCTION | default "false"}}' + vars: + BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build{{end}}' + + + generate:bindings: + label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}}) + summary: Generates bindings for the frontend + 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 + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + sources: + - "appicon.png" + generates: + - "darwin/icons.icns" + - "windows/icon.ico" + cmds: + - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico + + dev:frontend: + summary: Runs the frontend in development mode + dir: frontend + deps: + - task: install:frontend:deps + cmds: + - echo "Skipping frontend development mode" + + 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 . diff --git a/v3/examples/custom-protocol-example/build/appicon.png b/v3/examples/custom-protocol-example/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/custom-protocol-example/build/appicon.png differ diff --git a/v3/examples/custom-protocol-example/build/config.yml b/v3/examples/custom-protocol-example/build/config.yml new file mode 100644 index 000000000..8f2cb0348 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/config.yml @@ -0,0 +1,67 @@ +# 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: "My Company" # The name of the company + productName: "My Product" # The name of the application + productIdentifier: "com.mycompany.myproduct" # The unique product identifier + description: "A program that does X" # The application description + copyright: "(c) 2025, My Company" # Copyright text + comments: "Some Product Comments" # Comments + version: "0.0.1" # The application version + +# Dev mode configuration +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - 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) + +protocols: + - scheme: wailsexample + description: Wails Example Application Custom Protocol + +# Other data +other: + - name: My Other Data \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/build/darwin/Info.dev.plist b/v3/examples/custom-protocol-example/build/darwin/Info.dev.plist new file mode 100644 index 000000000..a46aa82d9 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/darwin/Info.dev.plist @@ -0,0 +1,43 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + custom-protocol-example + CFBundleIdentifier + com.mycompany.myproduct + CFBundleVersion + 0.0.1 + CFBundleGetInfoString + Some Product Comments + CFBundleShortVersionString + 0.0.1 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) 2025, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + CFBundleURLTypes + + + CFBundleURLName + wails.com.wailsexample + CFBundleURLSchemes + + wailsexample + + + + + \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/build/darwin/Info.plist b/v3/examples/custom-protocol-example/build/darwin/Info.plist new file mode 100644 index 000000000..d88fd1030 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/darwin/Info.plist @@ -0,0 +1,38 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + custom-protocol-example + CFBundleIdentifier + com.mycompany.myproduct + CFBundleVersion + 0.0.1 + CFBundleGetInfoString + Some Product Comments + CFBundleShortVersionString + 0.0.1 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) 2025, My Company + CFBundleURLTypes + + + CFBundleURLName + wails.com.wailsexample + CFBundleURLSchemes + + wailsexample + + + + + \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/build/darwin/Taskfile.yml b/v3/examples/custom-protocol-example/build/darwin/Taskfile.yml new file mode 100644 index 000000000..e456dbad5 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/darwin/Taskfile.yml @@ -0,0 +1,76 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Creates a production build of the application + deps: + - task: common:go:mod:tidy + - task: common:generate:icons + - task: common:build:frontend + cmds: + - go build {{.BUILD_FLAGS}} -o {{.OUTPUT}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{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=10.15" + CGO_LDFLAGS: "-mmacosx-version-min=10.15" + MACOSX_DEPLOYMENT_TARGET: "10.15" + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + 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: + - 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" + + package: + summary: Packages a production build of the application into a `.app` bundle + deps: + - task: build + vars: + PRODUCTION: "false" + 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,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS + - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents + - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app + + run: + cmds: + - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/{MacOS,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources + - 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}}' diff --git a/v3/examples/custom-protocol-example/build/darwin/icons.icns b/v3/examples/custom-protocol-example/build/darwin/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/custom-protocol-example/build/darwin/icons.icns differ diff --git a/v3/examples/custom-protocol-example/build/linux/Taskfile.yml b/v3/examples/custom-protocol-example/build/linux/Taskfile.yml new file mode 100644 index 000000000..a6115574a --- /dev/null +++ b/v3/examples/custom-protocol-example/build/linux/Taskfile.yml @@ -0,0 +1,113 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Linux + deps: + - task: common:go:mod:tidy + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application for Linux + deps: + - task: build + vars: + PRODUCTION: "true" + 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 + vars: + PRODUCTION: "true" + - task: generate:dotdesktop + cmds: + - cp {{.APP_BINARY}} {{.APP_NAME}} + - cp ../../appicon.png appicon.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: '../../appicon.png' + DESKTOP_FILE: '../{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../../bin' + + create:deb: + summary: Creates a deb package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:deb + + create:rpm: + summary: Creates a rpm package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:rpm + + create:aur: + summary: Creates a arch linux packager package + deps: + - task: build + vars: + PRODUCTION: "true" + 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: 'appicon' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/custom-protocol-example/build/linux/appimage/build.sh b/v3/examples/custom-protocol-example/build/linux/appimage/build.sh new file mode 100644 index 000000000..85901c34e --- /dev/null +++ b/v3/examples/custom-protocol-example/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/v3/examples/custom-protocol-example/build/linux/nfpm/nfpm.yaml b/v3/examples/custom-protocol-example/build/linux/nfpm/nfpm.yaml new file mode 100644 index 000000000..7356f2992 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/linux/nfpm/nfpm.yaml @@ -0,0 +1,50 @@ +# 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: "custom-protocol-example" +arch: ${GOARCH} +platform: "linux" +version: "0.0.1" +section: "default" +priority: "extra" +maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> +description: "A program that does X" +vendor: "My Company" +homepage: "https://wails.io" +license: "MIT" +release: "1" + +contents: + - src: "./bin/custom-protocol-example" + dst: "/usr/local/bin/custom-protocol-example" + - src: "./build/appicon.png" + dst: "/usr/share/icons/hicolor/128x128/apps/custom-protocol-example.png" + - src: "./build/linux/custom-protocol-example.desktop" + dst: "/usr/share/applications/custom-protocol-example.desktop" + +depends: + - gtk3 + - libwebkit2gtk + +# replaces: +# - foobar +# provides: +# - bar +# depends: +# - gtk3 +# - libwebkit2gtk +# recommends: +# - whatever +# suggests: +# - something-else +# conflicts: +# - not-foo +# - not-bar +# changelog: "changelog.yaml" +# scripts: +# preinstall: ./build/linux/nfpm/scripts/preinstall.sh +# postinstall: ./build/linux/nfpm/scripts/postinstall.sh +# preremove: ./build/linux/nfpm/scripts/preremove.sh +# postremove: ./build/linux/nfpm/scripts/postremove.sh diff --git a/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postinstall.sh b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postremove.sh b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preinstall.sh b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preremove.sh b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/custom-protocol-example/build/windows/Taskfile.yml b/v3/examples/custom-protocol-example/build/windows/Taskfile.yml new file mode 100644 index 000000000..805c1aa7f --- /dev/null +++ b/v3/examples/custom-protocol-example/build/windows/Taskfile.yml @@ -0,0 +1,57 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Windows + deps: + - task: common:go:mod:tidy + - task: common:generate:icons + cmds: + - task: generate:syso + - go build {{.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 .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: windows + CGO_ENABLED: 0 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application into a `.exe` bundle + cmds: + - task: create:nsis:installer + + 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 + vars: + PRODUCTION: "true" + cmds: + # Create the Microsoft WebView2 bootstrapper if it doesn't exist + - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis" + - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}.exe' diff --git a/v3/examples/custom-protocol-example/build/windows/icon.ico b/v3/examples/custom-protocol-example/build/windows/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/custom-protocol-example/build/windows/icon.ico differ diff --git a/v3/examples/custom-protocol-example/build/windows/info.json b/v3/examples/custom-protocol-example/build/windows/info.json new file mode 100644 index 000000000..71dfd6d99 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/windows/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.0.1" + }, + "info": { + "0000": { + "ProductVersion": "0.0.1", + "CompanyName": "My Company", + "FileDescription": "A program that does X", + "LegalCopyright": "(c) 2025, My Company", + "ProductName": "My Product", + "Comments": "Some Product Comments" + } + } +} \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/build/windows/nsis/project.nsi b/v3/examples/custom-protocol-example/build/windows/nsis/project.nsi new file mode 100644 index 000000000..5ff2eb085 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/windows/nsis/project.nsi @@ -0,0 +1,112 @@ +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 "custom-protocol-example" +## !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 "© now, 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 +#### +## 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. +InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). +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.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.deleteUninstaller +SectionEnd diff --git a/v3/examples/custom-protocol-example/build/windows/nsis/wails_tools.nsh b/v3/examples/custom-protocol-example/build/windows/nsis/wails_tools.nsh new file mode 100644 index 000000000..607fc4e85 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/windows/nsis/wails_tools.nsh @@ -0,0 +1,212 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "custom-protocol-example" +!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.0.1" +!endif +!ifndef INFO_COPYRIGHT + !define INFO_COPYRIGHT "(c) 2025, 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 REQUEST_EXECUTION_LEVEL + !define REQUEST_EXECUTION_LEVEL "admin" +!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 + 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" +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + DeleteRegKey HKLM "${UNINST_KEY}" +!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 \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/build/windows/wails.exe.manifest b/v3/examples/custom-protocol-example/build/windows/wails.exe.manifest new file mode 100644 index 000000000..21af92674 --- /dev/null +++ b/v3/examples/custom-protocol-example/build/windows/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/frontend/.gitignore b/v3/examples/custom-protocol-example/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/greetservice.js b/v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/greetservice.js new file mode 100644 index 000000000..0b93e6d75 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/index.js b/v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/examples/custom-protocol-example/frontend/dist/assets/index-BS9x21Y4.js b/v3/examples/custom-protocol-example/frontend/dist/assets/index-BS9x21Y4.js new file mode 100644 index 000000000..4f1023c59 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/dist/assets/index-BS9x21Y4.js @@ -0,0 +1,24 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))o(i);new MutationObserver(i=>{for(const r of i)if(r.type==="childList")for(const s of r.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&o(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const r={};return i.integrity&&(r.integrity=i.integrity),i.referrerPolicy&&(r.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?r.credentials="include":i.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function o(i){if(i.ep)return;i.ep=!0;const r=n(i);fetch(i.href,r)}})();const S="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function R(e=21){let t="",n=e|0;for(;n--;)t+=S[Math.random()*64|0];return t}const U=window.location.origin+"/wails/runtime",_=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let T=R();function k(e,t=""){return function(n,o=null){return I(e,n,t,o)}}async function I(e,t,n,o){var i,r;let s=new URL(U);s.searchParams.append("object",e.toString()),s.searchParams.append("method",t.toString()),o&&s.searchParams.append("args",JSON.stringify(o));let c={"x-wails-client-id":T};n&&(c["x-wails-window-name"]=n);let a=await fetch(s,{headers:c});if(!a.ok)throw new Error(await a.text());return((r=(i=a.headers.get("Content-Type"))===null||i===void 0?void 0:i.indexOf("application/json"))!==null&&r!==void 0?r:-1)!==-1?a.json():a.text()}k(_.System);const z=function(){var e,t,n,o,i;try{if(!((t=(e=window.chrome)===null||e===void 0?void 0:e.webview)===null||t===void 0)&&t.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((i=(o=(n=window.webkit)===null||n===void 0?void 0:n.messageHandlers)===null||o===void 0?void 0:o.external)===null||i===void 0)&&i.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` +%c⚠️ Browser Environment Detected %c + +%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. +More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development +`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function h(e){z==null||z(e)}function H(){return window._wails.environment.OS==="windows"}function W(){return!!window._wails.environment.Debug}function B(){return new MouseEvent("mousedown").buttons===0}function P(e){var t;return e.target instanceof HTMLElement?e.target:!(e.target instanceof HTMLElement)&&e.target instanceof Node&&(t=e.target.parentElement)!==null&&t!==void 0?t:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",X);const N=k(_.ContextMenu),j=0;function F(e,t,n,o){N(j,{id:e,x:t,y:n,data:o})}function X(e){const t=P(e),n=window.getComputedStyle(t).getPropertyValue("--custom-contextmenu").trim();if(n){e.preventDefault();const o=window.getComputedStyle(t).getPropertyValue("--custom-contextmenu-data");F(n,e.clientX,e.clientY,o)}else Y(e,t)}function Y(e,t){if(W())return;switch(window.getComputedStyle(t).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":e.preventDefault();return}if(t.isContentEditable)return;const n=window.getSelection(),o=n&&n.toString().length>0;if(o)for(let i=0;i{M=e,M||(w=p=!1,l())};window.addEventListener("mousedown",D,{capture:!0});window.addEventListener("mousemove",D,{capture:!0});window.addEventListener("mouseup",D,{capture:!0});for(const e of["click","contextmenu","dblclick"])window.addEventListener(e,A,{capture:!0});function A(e){(g||p)&&(e.stopImmediatePropagation(),e.stopPropagation(),e.preventDefault())}const C=0,V=1,L=2;function D(e){let t,n=e.buttons;switch(e.type){case"mousedown":t=C,E||(n=f|1<n!==e),t.length===0?d.delete(e.eventName):d.set(e.eventName,t))}window._wails=window._wails||{};window._wails.dispatchWailsEvent=ee;k(_.Events);class ${constructor(t,n=null){this.name=t,this.data=n}}function ee(e){let t=d.get(e.name);if(!t)return;let n=new $(e.name,e.data);"sender"in e&&(n.sender=e.sender),t=t.filter(o=>!o.dispatch(n)),t.length===0?d.delete(e.name):d.set(e.name,t)}function te(e,t,n){let o=d.get(e)||[];const i=new Q(e,t,n);return o.push(i),d.set(e,o),()=>Z(i)}function ne(e,t){return te(e,t,-1)}window._wails=window._wails||{};window._wails.invoke=h;h("wails:runtime:ready");document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("app");e?e.innerHTML=` +
+

Custom Protocol / Deep Link Test

+

+ This page demonstrates handling custom URL schemes (deep links). +

+

+ Example Link: + Try opening this URL (e.g., by pasting it into your browser's address bar or using open your-app-scheme://... in terminal): +
+ wailsexample://test/path?value=123&message=hello +

+ +
+ Received URL: +
Waiting for application to be opened via a custom URL...
+
+
+ `:console.error('Element with ID "app" not found.')});ne("frontend:ShowURL",e=>{console.log("frontend:ShowURL event received, data:",e),displayUrl(e.data)});window.displayUrl=function(e){const t=document.getElementById("received-url");t?t.textContent=e||"No URL received or an error occurred.":console.error("Element with ID 'received-url' not found in displayUrl.")}; diff --git a/v3/examples/custom-protocol-example/frontend/dist/assets/index-uDrhEpT0.css b/v3/examples/custom-protocol-example/frontend/dist/assets/index-uDrhEpT0.css new file mode 100644 index 000000000..37a5c205c --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/dist/assets/index-uDrhEpT0.css @@ -0,0 +1 @@ +:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;margin:0;background-color:#f7f7f7;color:#333;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:20px;box-sizing:border-box}h1{color:#0056b3;font-size:1.8em;margin-bottom:.8em}#app{width:100%;max-width:600px;margin:auto}.container{background-color:#fff;padding:25px 30px;border-radius:8px;box-shadow:0 2px 10px #0000001a;text-align:left}p{line-height:1.6;font-size:1em;margin-bottom:1em}a{color:#007bff;text-decoration:none}a:hover{text-decoration:underline}.url-display{margin-top:25px;padding:15px;background-color:#e9ecef;border:1px solid #ced4da;border-radius:4px;font-family:Courier New,Courier,monospace;font-size:.95em;word-break:break-all}.label{font-weight:700;display:block;margin-bottom:8px;color:#495057}.logo{height:6em;padding:1.5em;will-change:filter;transition:filter .3s}.logo:hover{filter:drop-shadow(0 0 2em #646cffaa)}.logo.vanilla:hover{filter:drop-shadow(0 0 2em #f7df1eaa)}.card{padding:2em}.read-the-docs{color:#888}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}} diff --git a/v3/examples/custom-protocol-example/frontend/dist/index.html b/v3/examples/custom-protocol-example/frontend/dist/index.html new file mode 100644 index 000000000..126962c69 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/dist/index.html @@ -0,0 +1,14 @@ + + + + + + + Vite App + + + + +
+ + diff --git a/v3/examples/custom-protocol-example/frontend/dist/vite.svg b/v3/examples/custom-protocol-example/frontend/dist/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/dist/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/frontend/index.html b/v3/examples/custom-protocol-example/frontend/index.html new file mode 100644 index 000000000..72ba3a8b3 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/v3/examples/custom-protocol-example/frontend/package-lock.json b/v3/examples/custom-protocol-example/frontend/package-lock.json new file mode 100644 index 000000000..2b2b28cc5 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/package-lock.json @@ -0,0 +1,1017 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@wailsio/runtime": "^3.0.0-alpha.66" + }, + "devDependencies": { + "vite": "^6.3.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz", + "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz", + "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", + "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz", + "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz", + "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz", + "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz", + "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz", + "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz", + "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz", + "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz", + "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz", + "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz", + "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz", + "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz", + "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz", + "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz", + "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz", + "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz", + "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", + "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wailsio/runtime": { + "version": "3.0.0-alpha.66", + "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.66.tgz", + "integrity": "sha512-ENLu8rn1griL1gFHJqkq1i+BVxrrA0JPJHYneUJYuf/s54kjuQViW0RKDEe/WTDo56ABpfykrd/T8OYpPUyXUw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", + "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.0", + "@rollup/rollup-android-arm64": "4.41.0", + "@rollup/rollup-darwin-arm64": "4.41.0", + "@rollup/rollup-darwin-x64": "4.41.0", + "@rollup/rollup-freebsd-arm64": "4.41.0", + "@rollup/rollup-freebsd-x64": "4.41.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", + "@rollup/rollup-linux-arm-musleabihf": "4.41.0", + "@rollup/rollup-linux-arm64-gnu": "4.41.0", + "@rollup/rollup-linux-arm64-musl": "4.41.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-musl": "4.41.0", + "@rollup/rollup-linux-s390x-gnu": "4.41.0", + "@rollup/rollup-linux-x64-gnu": "4.41.0", + "@rollup/rollup-linux-x64-musl": "4.41.0", + "@rollup/rollup-win32-arm64-msvc": "4.41.0", + "@rollup/rollup-win32-ia32-msvc": "4.41.0", + "@rollup/rollup-win32-x64-msvc": "4.41.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/v3/examples/custom-protocol-example/frontend/package.json b/v3/examples/custom-protocol-example/frontend/package.json new file mode 100644 index 000000000..64d7435a8 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/package.json @@ -0,0 +1,17 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^6.3.5" + }, + "dependencies": { + "@wailsio/runtime": "^3.0.0-alpha.66" + } +} diff --git a/v3/examples/custom-protocol-example/frontend/public/vite.svg b/v3/examples/custom-protocol-example/frontend/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/frontend/src/counter.js b/v3/examples/custom-protocol-example/frontend/src/counter.js new file mode 100644 index 000000000..881e2d7ad --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/src/counter.js @@ -0,0 +1,9 @@ +export function setupCounter(element) { + let counter = 0 + const setCounter = (count) => { + counter = count + element.innerHTML = `count is ${counter}` + } + element.addEventListener('click', () => setCounter(counter + 1)) + setCounter(0) +} diff --git a/v3/examples/custom-protocol-example/frontend/src/javascript.svg b/v3/examples/custom-protocol-example/frontend/src/javascript.svg new file mode 100644 index 000000000..f9abb2b72 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/src/javascript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/custom-protocol-example/frontend/src/main.js b/v3/examples/custom-protocol-example/frontend/src/main.js new file mode 100644 index 000000000..fd11d82fa --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/src/main.js @@ -0,0 +1,45 @@ +import './style.css' +import { Events } from '@wailsio/runtime' + +document.addEventListener('DOMContentLoaded', () => { + const appDiv = document.getElementById('app'); + if (appDiv) { + appDiv.innerHTML = ` +
+

Custom Protocol / Deep Link Test

+

+ This page demonstrates handling custom URL schemes (deep links). +

+

+ Example Link: + Try opening this URL (e.g., by pasting it into your browser's address bar or using open your-app-scheme://... in terminal): +
+ wailsexample://test/path?value=123&message=hello +

+ +
+ Received URL: +
Waiting for application to be opened via a custom URL...
+
+
+ `; + } else { + console.error('Element with ID "app" not found.'); + } +}); + +// Listen for the event from Go +Events.On('frontend:ShowURL', (e) => { + console.log('frontend:ShowURL event received, data:', e); + displayUrl(e.data); +}); + +// Make displayUrl available globally just in case, though direct call from event is better +window.displayUrl = function(url) { + const urlElement = document.getElementById('received-url'); + if (urlElement) { + urlElement.textContent = url || "No URL received or an error occurred."; + } else { + console.error("Element with ID 'received-url' not found in displayUrl."); + } +} diff --git a/v3/examples/custom-protocol-example/frontend/src/style.css b/v3/examples/custom-protocol-example/frontend/src/style.css new file mode 100644 index 000000000..0fb047c33 --- /dev/null +++ b/v3/examples/custom-protocol-example/frontend/src/style.css @@ -0,0 +1,142 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + margin: 0; /* Reset default margin */ + background-color: #f7f7f7; + color: #333; + display: flex; /* Use flexbox to center content */ + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically */ + min-height: 100vh; /* Full viewport height */ + padding: 20px; /* Add some padding around the content */ + box-sizing: border-box; /* Ensure padding doesn't expand body beyond viewport */ +} + +h1 { + color: #0056b3; + font-size: 1.8em; + margin-bottom: 0.8em; +} + +#app { + width: 100%; + max-width: 600px; + margin: auto; /* This also helps in centering if body flex isn't enough or overridden */ +} + +.container { + background-color: #fff; + padding: 25px 30px; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + text-align: left; +} + +p { + line-height: 1.6; + font-size: 1em; + margin-bottom: 1em; +} + +a { + color: #007bff; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.url-display { + margin-top: 25px; + padding: 15px; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: 4px; + font-family: 'Courier New', Courier, monospace; + font-size: 0.95em; + word-break: break-all; +} + +.label { + font-weight: bold; + display: block; + margin-bottom: 8px; + color: #495057; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/v3/examples/custom-protocol-example/greetservice.go b/v3/examples/custom-protocol-example/greetservice.go new file mode 100644 index 000000000..8972c39cd --- /dev/null +++ b/v3/examples/custom-protocol-example/greetservice.go @@ -0,0 +1,7 @@ +package main + +type GreetService struct{} + +func (g *GreetService) Greet(name string) string { + return "Hello " + name + "!" +} diff --git a/v3/examples/custom-protocol-example/main.go b/v3/examples/custom-protocol-example/main.go new file mode 100644 index 000000000..f5c5db38b --- /dev/null +++ b/v3/examples/custom-protocol-example/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "embed" + _ "embed" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed all:frontend/dist +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + app := application.New(application.Options{ + Name: "custom-protocol-example", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Listen for the system event indicating the app was launched with a URL + app.Event.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) { + app.Event.Emit("frontend:ShowURL", e.Context().URL()) + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + _ = app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + // Create a goroutine that emits an event containing the current time every second. + // The frontend can listen to this event and update the UI accordingly. + go func() { + for { + now := time.Now().Format(time.RFC1123) + app.Event.Emit("time", now) + time.Sleep(time.Second) + } + }() + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/dev/.gitignore b/v3/examples/dev/.gitignore new file mode 100644 index 000000000..c2a88322f --- /dev/null +++ b/v3/examples/dev/.gitignore @@ -0,0 +1 @@ +.task \ No newline at end of file diff --git a/v3/examples/dev/README.md b/v3/examples/dev/README.md new file mode 100644 index 000000000..5dcc421f7 --- /dev/null +++ b/v3/examples/dev/README.md @@ -0,0 +1,4 @@ +# Dev Example + +**NOTE**: This example is currently a work in progress. It is not yet ready for use. + diff --git a/v3/examples/dev/Taskfile.yml b/v3/examples/dev/Taskfile.yml new file mode 100644 index 000000000..8d2d46716 --- /dev/null +++ b/v3/examples/dev/Taskfile.yml @@ -0,0 +1,183 @@ +version: '3' + +vars: + APP_NAME: "dev{{exeExt}}" + +tasks: + + pre-build: + summary: Pre-build hooks + + post-build: + summary: Post-build hooks + + install-frontend-deps: + summary: Install frontend dependencies + 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 + + build-frontend: + summary: Build the frontend project + dir: frontend + sources: + - src/* + generates: + - dist/* + deps: + - install-frontend-deps + cmds: + - npm run build + + build:darwin: + summary: Builds the application + platforms: + - darwin + cmds: + - task: pre-build + - task: build-frontend + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + - task: post-build + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" + + build:backend:darwin: + summary: Builds the application + platforms: + - darwin + cmds: + - task: pre-build + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + - task: post-build + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" + + build:windows: + summary: Builds the application for Windows + platforms: + - windows + cmds: + - task: pre-build + - task: build-frontend + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + - task: post-build + + + build:backend:windows: + summary: Builds the backend application for Windows + platforms: + - windows + cmds: + - task: pre-build + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + - task: post-build + + build: + summary: Builds the application + watch: true + sources: + - main.go + cmds: + - task: build:darwin + - task: build:windows + - task: run + + build:backend: + summary: Builds the backend application + cmds: + - task: build:backend:darwin + - task: build:backend:windows + + generate-icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + cmds: + # Generates both .ico and .icns files + - wails generate icons -input appicon.png + + build-app-prod-darwin: + summary: Creates a production build of the application + cmds: + - task: pre-build + - task: build-frontend + - GOOS=darwin GOARCH={{.ARCH}} go build -tags production -ldflags="-w -s" -o build/bin/{{.APP_NAME}} + - task: post-build + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" + vars: + ARCH: $GOARCH + + frontend:dev: + summary: Runs the frontend in development mode + deps: + - task: install-frontend-deps + dir: frontend + cmds: + - npm run dev + + run: + summary: Runs the application + cmds: + - ./bin/{{.APP_NAME}} + + dev: + summary: Runs the application in development mode + watch: true + preconditions: + - sh: 'wails3 tool checkport -p 5173' + msg: "Vite does not appear to be running. Please run `wails3 task frontend:dev` in another terminal." + cmds: + - task: build:backend + + create-app-bundle: + summary: Builds a `.app` bundle + cmds: + - mkdir -p {{.APP_NAME}}.app/Contents/{MacOS,Resources} + - cp build/icons.icns {{.APP_NAME}}.app/Contents/Resources + - cp build/bin/{{.APP_NAME}} {{.APP_NAME}}.app/Contents/MacOS + - cp build/Info.plist {{.APP_NAME}}.app/Contents + + package-darwin-arm64: + summary: Packages a production build of the application into a `.app` bundle + platform: darwin + deps: + - task: build-app-prod-darwin + vars: + ARCH: arm64 + - generate-icons + cmds: + - task: create-app-bundle + + generate:syso: + dir: build + platform: windows + cmds: + - wails generate syso -arch {{.ARCH}} -icon icon.ico -manifest wails.exe.manifest -info info.json + vars: + ARCH: $GOARCH + + package:windows: + summary: Packages a production build of the application into a `.exe` bundle + platform: windows + deps: + - generate-icons + cmds: + - task: generate:syso + vars: + ARCH: amd64 + - go build -tags production -ldflags="-w -s -H windowsgui" -o bin/{{.APP_NAME}}.exe + - powershell Remove-item *.syso diff --git a/v3/examples/dev/build/Info.dev.plist b/v3/examples/dev/build/Info.dev.plist new file mode 100644 index 000000000..0c0ed6032 --- /dev/null +++ b/v3/examples/dev/build/Info.dev.plist @@ -0,0 +1,32 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product Name + CFBundleExecutable + dev + CFBundleIdentifier + com.wails.dev + CFBundleVersion + v1.0.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + v1.0.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) 2023 My Company Name + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/v3/examples/dev/build/Info.plist b/v3/examples/dev/build/Info.plist new file mode 100644 index 000000000..f9ea24900 --- /dev/null +++ b/v3/examples/dev/build/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product Name + CFBundleExecutable + dev + CFBundleIdentifier + com.wails.dev + CFBundleVersion + v1.0.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + v1.0.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) 2023 My Company Name + + \ No newline at end of file diff --git a/v3/examples/dev/build/appicon.png b/v3/examples/dev/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/dev/build/appicon.png differ diff --git a/v3/examples/dev/build/icons.icns b/v3/examples/dev/build/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/dev/build/icons.icns differ diff --git a/v3/examples/dev/frontend/dist/assets/index-3635012e.css b/v3/examples/dev/frontend/dist/assets/index-3635012e.css new file mode 100644 index 000000000..14cfb027f --- /dev/null +++ b/v3/examples/dev/frontend/dist/assets/index-3635012e.css @@ -0,0 +1 @@ +:root{font-family:Inter,Avenir,Helvetica,Arial,sans-serif;font-size:16px;line-height:24px;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}.card{padding:2em}#app{max-width:1280px;margin:0 auto;padding:2rem;text-align:center}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}.logo.svelte-c9fbf7{height:6em;padding:1.5em;will-change:filter}.logo.svelte-c9fbf7:hover{filter:drop-shadow(0 0 2em #646cffaa)}.logo.svelte.svelte-c9fbf7:hover{filter:drop-shadow(0 0 2em #ff3e00aa)}.read-the-docs.svelte-c9fbf7{color:#888} diff --git a/v3/examples/dev/frontend/dist/assets/index-9076c63b.js b/v3/examples/dev/frontend/dist/assets/index-9076c63b.js new file mode 100644 index 000000000..e2b665b28 --- /dev/null +++ b/v3/examples/dev/frontend/dist/assets/index-9076c63b.js @@ -0,0 +1 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(o){if(o.ep)return;o.ep=!0;const s=n(o);fetch(o.href,s)}})();function v(){}function I(e){return e()}function W(){return Object.create(null)}function A(e){e.forEach(I)}function K(e){return typeof e=="function"}function F(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}let S;function X(e,t){return S||(S=document.createElement("a")),S.href=t,e===S.href}function Y(e){return Object.keys(e).length===0}function c(e,t){e.appendChild(t)}function V(e,t,n){e.insertBefore(t,n||null)}function M(e){e.parentNode&&e.parentNode.removeChild(e)}function a(e){return document.createElement(e)}function k(e){return document.createTextNode(e)}function w(){return k(" ")}function Z(e,t,n,r){return e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)}function u(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function ee(e){return Array.from(e.childNodes)}function te(e,t){t=""+t,e.data!==t&&(e.data=t)}let q;function E(e){q=e}const $=[],B=[];let y=[];const H=[],ne=Promise.resolve();let j=!1;function re(){j||(j=!0,ne.then(z))}function P(e){y.push(e)}const N=new Set;let g=0;function z(){if(g!==0)return;const e=q;do{try{for(;g<$.length;){const t=$[g];g++,E(t),oe(t.$$)}}catch(t){throw $.length=0,g=0,t}for(E(null),$.length=0,g=0;B.length;)B.pop()();for(let t=0;te.indexOf(r)===-1?t.push(r):n.push(r)),n.forEach(r=>r()),y=t}const C=new Set;let ie;function D(e,t){e&&e.i&&(C.delete(e),e.i(t))}function le(e,t,n,r){if(e&&e.o){if(C.has(e))return;C.add(e),ie.c.push(()=>{C.delete(e),r&&(n&&e.d(1),r())}),e.o(t)}else r&&r()}function ce(e){e&&e.c()}function G(e,t,n,r){const{fragment:o,after_update:s}=e.$$;o&&o.m(t,n),r||P(()=>{const i=e.$$.on_mount.map(I).filter(K);e.$$.on_destroy?e.$$.on_destroy.push(...i):A(i),e.$$.on_mount=[]}),s.forEach(P)}function J(e,t){const n=e.$$;n.fragment!==null&&(se(n.after_update),A(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function fe(e,t){e.$$.dirty[0]===-1&&($.push(e),re(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const _=x.length?x[0]:d;return l.ctx&&o(l.ctx[f],l.ctx[f]=_)&&(!l.skip_bound&&l.bound[f]&&l.bound[f](_),b&&fe(e,f)),d}):[],l.update(),b=!0,A(l.before_update),l.fragment=r?r(l.ctx):!1,t.target){if(t.hydrate){const f=ee(t.target);l.fragment&&l.fragment.l(f),f.forEach(M)}else l.fragment&&l.fragment.c();t.intro&&D(e.$$.fragment),G(e,t.target,t.anchor,t.customElement),z()}E(p)}class R{$destroy(){J(this,1),this.$destroy=v}$on(t,n){if(!K(n))return v;const r=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return r.push(n),()=>{const o=r.indexOf(n);o!==-1&&r.splice(o,1)}}$set(t){this.$$set&&!Y(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const ue="/assets/svelte-a39f39b7.svg";function ae(e){let t,n,r,o,s;return{c(){t=a("button"),n=k("count is "),r=k(e[0])},m(i,h){V(i,t,h),c(t,n),c(t,r),o||(s=Z(t,"click",e[1]),o=!0)},p(i,[h]){h&1&&te(r,i[0])},i:v,o:v,d(i){i&&M(t),o=!1,s()}}}function de(e,t,n){let r=0;return[r,()=>{n(0,r+=1)}]}class he extends R{constructor(t){super(),Q(this,t,de,ae,F,{})}}function pe(e){let t,n,r,o,s,i,h,p,l,b,f,d,x,_,T,L,O;return d=new he({}),{c(){t=a("main"),n=a("div"),r=a("a"),r.innerHTML='',o=w(),s=a("a"),i=a("img"),p=w(),l=a("h1"),l.textContent="Wails + Svelte",b=w(),f=a("div"),ce(d.$$.fragment),x=w(),_=a("p"),_.innerHTML='Check out SvelteKit, the official Svelte app framework powered by Vite!',T=w(),L=a("p"),L.textContent="Click on the Wails and Svelte logos to learn more",u(r,"href","https://wails.io"),u(r,"target","_blank"),u(r,"rel","noreferrer"),X(i.src,h=ue)||u(i,"src",h),u(i,"class","logo svelte svelte-c9fbf7"),u(i,"alt","Svelte Logo"),u(s,"href","https://svelte.dev"),u(s,"target","_blank"),u(s,"rel","noreferrer"),u(f,"class","card"),u(L,"class","read-the-docs svelte-c9fbf7")},m(m,U){V(m,t,U),c(t,n),c(n,r),c(n,o),c(n,s),c(s,i),c(t,p),c(t,l),c(t,b),c(t,f),G(d,f,null),c(t,x),c(t,_),c(t,T),c(t,L),O=!0},p:v,i(m){O||(D(d.$$.fragment,m),O=!0)},o(m){le(d.$$.fragment,m),O=!1},d(m){m&&M(t),J(d)}}}class me extends R{constructor(t){super(),Q(this,t,null,pe,F,{})}}new me({target:document.getElementById("app")}); diff --git a/v3/examples/dev/frontend/dist/assets/svelte-a39f39b7.svg b/v3/examples/dev/frontend/dist/assets/svelte-a39f39b7.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/examples/dev/frontend/dist/assets/svelte-a39f39b7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/dev/frontend/dist/index.html b/v3/examples/dev/frontend/dist/index.html new file mode 100644 index 000000000..c4380af7b --- /dev/null +++ b/v3/examples/dev/frontend/dist/index.html @@ -0,0 +1,15 @@ + + + + + + + Wails + Svelte + + + + +
+ + + diff --git a/v3/examples/dev/frontend/dist/wails.png b/v3/examples/dev/frontend/dist/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/dev/frontend/dist/wails.png differ diff --git a/v3/examples/dev/frontend/index.html b/v3/examples/dev/frontend/index.html new file mode 100644 index 000000000..1ea50f904 --- /dev/null +++ b/v3/examples/dev/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Wails + Svelte + + +
+ + + diff --git a/v3/examples/dev/frontend/jsconfig.json b/v3/examples/dev/frontend/jsconfig.json new file mode 100644 index 000000000..e596c5823 --- /dev/null +++ b/v3/examples/dev/frontend/jsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "moduleResolution": "Node", + "target": "ESNext", + "module": "ESNext", + /** + * svelte-preprocess cannot figure out whether you have + * a value or a type, so tell TypeScript to enforce using + * `import type` instead of `import` for Types. + */ + "importsNotUsedAsValues": "error", + "isolatedModules": true, + "resolveJsonModule": true, + /** + * To have warnings / errors of the Svelte compiler at the + * correct position, enable source maps by default. + */ + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable this if you'd like to use dynamic types. + */ + "checkJs": true + }, + /** + * Use global.d.ts instead of compilerOptions.types + * to avoid limiting type declarations. + */ + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] +} diff --git a/v3/examples/dev/frontend/package-lock.json b/v3/examples/dev/frontend/package-lock.json new file mode 100644 index 000000000..309115bf9 --- /dev/null +++ b/v3/examples/dev/frontend/package-lock.json @@ -0,0 +1,685 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.0.0", + "svelte": "^3.54.0", + "vite": "^4.5.2" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz", + "integrity": "sha512-UJKsFNwhzCVuiZd06jM/psscyNJNDwjQC+qIeb7GBJK9iWeQCcIyfcPWDvbCudfcJggY9jtxJeeaZH7uny93FQ==", + "dev": true, + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^1.0.3", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.2", + "svelte-hmr": "^0.15.3", + "vitefu": "^0.2.4" + }, + "engines": { + "node": "^14.18.0 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.54.0 || ^4.0.0", + "vite": "^4.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.3.tgz", + "integrity": "sha512-Khdl5jmmPN6SUsVuqSXatKpQTMIifoQPDanaxC84m9JxIibWvSABJyHpyys0Z+1yYrxY5TTEQm+6elh0XCMaOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^14.18.0 || >= 16" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.2.0", + "svelte": "^3.54.0 || ^4.0.0", + "vite": "^4.0.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/magic-string": { + "version": "0.30.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", + "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", + "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz", + "integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "3.59.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz", + "integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/svelte-hmr": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", + "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", + "dev": true, + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/vite": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz", + "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz", + "integrity": "sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==", + "dev": true, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + } + } +} diff --git a/v3/examples/dev/frontend/package.json b/v3/examples/dev/frontend/package.json new file mode 100644 index 000000000..ed218cf99 --- /dev/null +++ b/v3/examples/dev/frontend/package.json @@ -0,0 +1,16 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.0.0", + "svelte": "^3.54.0", + "vite": "^4.5.2" + } +} \ No newline at end of file diff --git a/v3/examples/dev/frontend/public/wails.png b/v3/examples/dev/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/dev/frontend/public/wails.png differ diff --git a/v3/examples/dev/frontend/src/App.svelte b/v3/examples/dev/frontend/src/App.svelte new file mode 100644 index 000000000..539c395dd --- /dev/null +++ b/v3/examples/dev/frontend/src/App.svelte @@ -0,0 +1,45 @@ + + +
+ +

Wails + Svelte

+ +
+ +
+ +

+ Check out SvelteKit, the official Svelte app framework powered by Vite! +

+ +

+ Click on the Wails and Svelte logos to learn more +

+
+ + diff --git a/v3/examples/dev/frontend/src/app.css b/v3/examples/dev/frontend/src/app.css new file mode 100644 index 000000000..bcc7233dd --- /dev/null +++ b/v3/examples/dev/frontend/src/app.css @@ -0,0 +1,81 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/v3/examples/dev/frontend/src/assets/svelte.svg b/v3/examples/dev/frontend/src/assets/svelte.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/examples/dev/frontend/src/assets/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/dev/frontend/src/lib/Counter.svelte b/v3/examples/dev/frontend/src/lib/Counter.svelte new file mode 100644 index 000000000..e45f90310 --- /dev/null +++ b/v3/examples/dev/frontend/src/lib/Counter.svelte @@ -0,0 +1,10 @@ + + + diff --git a/v3/examples/dev/frontend/src/main.js b/v3/examples/dev/frontend/src/main.js new file mode 100644 index 000000000..8a909a15a --- /dev/null +++ b/v3/examples/dev/frontend/src/main.js @@ -0,0 +1,8 @@ +import './app.css' +import App from './App.svelte' + +const app = new App({ + target: document.getElementById('app'), +}) + +export default app diff --git a/v3/examples/dev/frontend/src/vite-env.d.ts b/v3/examples/dev/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..4078e7476 --- /dev/null +++ b/v3/examples/dev/frontend/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/v3/examples/dev/frontend/vite.config.js b/v3/examples/dev/frontend/vite.config.js new file mode 100644 index 000000000..d70196943 --- /dev/null +++ b/v3/examples/dev/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()], +}) diff --git a/v3/examples/dev/go.mod b/v3/examples/dev/go.mod new file mode 100644 index 000000000..f91cd7357 --- /dev/null +++ b/v3/examples/dev/go.mod @@ -0,0 +1,50 @@ +module changeme + +go 1.24.0 + +require github.com/wailsapp/wails/v3 v3.0.0-alpha.0 + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) + +replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/dev/go.sum b/v3/examples/dev/go.sum new file mode 100644 index 000000000..cbadfe003 --- /dev/null +++ b/v3/examples/dev/go.sum @@ -0,0 +1,146 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/v3/examples/dev/main.go b/v3/examples/dev/main.go new file mode 100644 index 000000000..111273721 --- /dev/null +++ b/v3/examples/dev/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed frontend/dist +var assets embed.FS + +func main() { + app := application.New(application.Options{ + Name: "dev", + Description: "A demo of using raw HTML & CSS", + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + // Create window + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Plain Bundle", + CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + + URL: "/", + }) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/dialogs-basic/.hidden_file b/v3/examples/dialogs-basic/.hidden_file new file mode 100644 index 000000000..e69de29bb diff --git a/v3/examples/dialogs-basic/README.md b/v3/examples/dialogs-basic/README.md new file mode 100644 index 000000000..c03911376 --- /dev/null +++ b/v3/examples/dialogs-basic/README.md @@ -0,0 +1,36 @@ +# Dialog Test Application + +This application is designed to test macOS file dialog functionality across different versions of macOS. It provides a comprehensive suite of tests for various dialog features and configurations. + +## Features Tested + +1. Basic file open dialog +2. Single extension filter +3. Multiple extension filter +4. Multiple file selection +5. Directory selection +6. Save dialog with extension +7. Complex filters +8. Hidden files +9. Default directory +10. Full featured dialog with all options + +## Running the Tests + +```bash +go run main.go +``` + +## Test Results + +When running tests: +- Each test will show the selected file(s) and their types +- For multiple selections, all selected files will be listed +- Errors will be displayed in an error dialog +- The application logs debug information to help track issues + +## Notes + +- This test application is primarily for development and testing purposes +- It can be used to verify dialog behavior across different macOS versions +- The tests are designed to not interfere with CI pipelines diff --git a/v3/examples/dialogs-basic/main.go b/v3/examples/dialogs-basic/main.go new file mode 100644 index 000000000..8df5a1c37 --- /dev/null +++ b/v3/examples/dialogs-basic/main.go @@ -0,0 +1,260 @@ +package main + +import ( + "fmt" + "log" + "log/slog" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Dialog Test", + Description: "Test application for macOS dialogs", + Logger: application.DefaultLogger(slog.LevelDebug), + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create main window + mainWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Dialog Tests", + Width: 800, + Height: 600, + MinWidth: 800, + MinHeight: 600, + }) + mainWindow.SetAlwaysOnTop(true) + + // Create main menu + menu := app.NewMenu() + app.Menu.Set(menu) + menu.AddRole(application.AppMenu) + menu.AddRole(application.EditMenu) + menu.AddRole(application.WindowMenu) + + // Add test menu + testMenu := menu.AddSubmenu("Tests") + + // Test 1: Basic file open with no filters (no window) + testMenu.Add("1. Basic Open (No Window)").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + PromptForSingleSelection() + showResult("Basic Open", result, err, nil) + }) + + // Test 1b: Basic file open with window + testMenu.Add("1b. Basic Open (With Window)").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Basic Open", result, err, mainWindow) + }) + + // Test 2: Open with single extension filter + testMenu.Add("2. Single Filter").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("Text Files", "*.txt"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Single Filter", result, err, mainWindow) + }) + + // Test 3: Open with multiple extension filter + testMenu.Add("3. Multiple Filter").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("Documents", "*.txt;*.md;*.doc;*.docx"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Multiple Filter", result, err, mainWindow) + }) + + // Test 4: Multiple file selection + testMenu.Add("4. Multiple Selection").OnClick(func(ctx *application.Context) { + results, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("Images", "*.png;*.jpg;*.jpeg"). + AttachToWindow(mainWindow). + PromptForMultipleSelection() + if err != nil { + showError("Multiple Selection", err, mainWindow) + return + } + showResults("Multiple Selection", results, mainWindow) + }) + + // Test 5: Directory selection + testMenu.Add("5. Directory Selection").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseDirectories(true). + CanChooseFiles(false). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Directory Selection", result, err, mainWindow) + }) + + // Test 6: Save dialog with extension + testMenu.Add("6. Save Dialog").OnClick(func(ctx *application.Context) { + result, err := application.SaveFileDialog(). + SetFilename("test.txt"). + AddFilter("Text Files", "*.txt"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Save Dialog", result, err, mainWindow) + }) + + // Test 7: Complex filters + testMenu.Add("7. Complex Filters").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("All Documents", "*.txt;*.md;*.doc;*.docx;*.pdf"). + AddFilter("Text Files", "*.txt"). + AddFilter("Markdown", "*.md"). + AddFilter("Word Documents", "*.doc;*.docx"). + AddFilter("PDF Files", "*.pdf"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Complex Filters", result, err, mainWindow) + }) + + // Test 8: Hidden files + testMenu.Add("8. Show Hidden").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + ShowHiddenFiles(true). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Show Hidden", result, err, mainWindow) + }) + + // Test 9: Default directory + testMenu.Add("9. Default Directory").OnClick(func(ctx *application.Context) { + home, _ := os.UserHomeDir() + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + SetDirectory(home). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Default Directory", result, err, mainWindow) + }) + + // Test 10: Full featured dialog + testMenu.Add("10. Full Featured").OnClick(func(ctx *application.Context) { + home, _ := os.UserHomeDir() + dialog := application.OpenFileDialog(). + SetTitle("Full Featured Dialog"). + SetDirectory(home). + CanChooseFiles(true). + CanCreateDirectories(true). + ShowHiddenFiles(true). + ResolvesAliases(true). + AllowsOtherFileTypes(true). + AttachToWindow(mainWindow) + + if runtime.GOOS == "darwin" { + dialog.SetMessage("Please select files") + } + + dialog.AddFilter("All Supported", "*.txt;*.md;*.pdf;*.png;*.jpg") + dialog.AddFilter("Documents", "*.txt;*.md;*.pdf") + dialog.AddFilter("Images", "*.png;*.jpg;*.jpeg") + + results, err := dialog.PromptForMultipleSelection() + if err != nil { + showError("Full Featured", err, mainWindow) + return + } + showResults("Full Featured", results, mainWindow) + }) + + // Show the window + mainWindow.Show() + + // Run the app + if err := app.Run(); err != nil { + log.Fatal(err) + } +} + +func showResult(test string, result string, err error, window *application.WebviewWindow) { + if err != nil { + showError(test, err, window) + return + } + if result == "" { + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage("No file selected") + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() + return + } + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage(fmt.Sprintf("Selected: %s\nType: %s", result, getFileType(result))) + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() +} + +func showResults(test string, results []string, window *application.WebviewWindow) { + if len(results) == 0 { + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage("No files selected") + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() + return + } + var message strings.Builder + message.WriteString(fmt.Sprintf("Selected %d files:\n\n", len(results))) + for _, result := range results { + message.WriteString(fmt.Sprintf("%s (%s)\n", result, getFileType(result))) + } + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage(message.String()) + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() +} + +func showError(test string, err error, window *application.WebviewWindow) { + dialog := application.ErrorDialog(). + SetTitle(test). + SetMessage(fmt.Sprintf("Error: %v", err)) + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() +} + +func getFileType(path string) string { + if path == "" { + return "unknown" + } + ext := strings.ToLower(filepath.Ext(path)) + if ext == "" { + if fi, err := os.Stat(path); err == nil && fi.IsDir() { + return "directory" + } + return "no extension" + } + return ext +} diff --git a/v3/examples/dialogs-basic/test.txt b/v3/examples/dialogs-basic/test.txt new file mode 100644 index 000000000..64b89ccaf --- /dev/null +++ b/v3/examples/dialogs-basic/test.txt @@ -0,0 +1 @@ +This is a sample text file to test filtering. \ No newline at end of file diff --git a/v3/examples/dialogs-basic/wails-logo-small.jpg b/v3/examples/dialogs-basic/wails-logo-small.jpg new file mode 100644 index 000000000..29cb1129e Binary files /dev/null and b/v3/examples/dialogs-basic/wails-logo-small.jpg differ diff --git a/v3/examples/dialogs-basic/wails-logo-small.png b/v3/examples/dialogs-basic/wails-logo-small.png new file mode 100644 index 000000000..cbd40bf90 Binary files /dev/null and b/v3/examples/dialogs-basic/wails-logo-small.png differ diff --git a/v3/examples/dialogs/README.md b/v3/examples/dialogs/README.md new file mode 100644 index 000000000..d80afc91a --- /dev/null +++ b/v3/examples/dialogs/README.md @@ -0,0 +1,33 @@ +# Dialogs Example + +This example is a comprehensive example of using dialogs in Wails. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run main.go +``` + +## Building the example + +To build the example in debug mode, simply run the following command: + +```bash +wails3 task build +``` + +To build the example to use application icons, simply run the following command: + +```bash +wails3 task package +``` + +# Status + +| Platform | Status | +|----------|----------------| +| Mac | Mostly Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/dialogs/Taskfile.yml b/v3/examples/dialogs/Taskfile.yml new file mode 100644 index 000000000..90ac01145 --- /dev/null +++ b/v3/examples/dialogs/Taskfile.yml @@ -0,0 +1,107 @@ +version: '3' + +vars: + APP_NAME: "buildtest{{exeExt}}" + + +tasks: + + build: + summary: Builds the application + cmds: + - go build -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" + + package: + summary: Packages a production build of the application into a `.app` or `.exe` bundle + cmds: + - task: package:darwin + - task: package:windows + +# ------------------------------------------------------------------------------ + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + cmds: + # Generates both .ico and .icns files + - wails3 generate icons -input appicon.png + + build:production:darwin: + summary: Creates a production build of the application + cmds: + - GOOS=darwin GOARCH={{.GOARCH}} go build -tags production -ldflags="-w -s" -o build/bin/{{.APP_NAME}} + env: + CGO_CFLAGS: "-mmacosx-version-min=10.13" + CGO_LDFLAGS: "-mmacosx-version-min=10.13" + MACOSX_DEPLOYMENT_TARGET: "10.13" + GOARCH: '{{.GOARCH}}' + + generate:app_bundle: + summary: Builds a `.app` bundle + cmds: + - mkdir -p {{.APP_NAME}}.app/Contents/{MacOS,Resources} + - cp build/icons.icns {{.APP_NAME}}.app/Contents/Resources + - cp build/bin/{{.APP_NAME}} {{.APP_NAME}}.app/Contents/MacOS + - cp build/Info.plist {{.APP_NAME}}.app/Contents + + package:darwin:arm64: + summary: Packages a production build of the application into a `.app` bundle + platforms: + - darwin + deps: + - task: build:production:darwin + vars: + ARCH: arm64 + - generate:icons + cmds: + - task: generate:app_bundle + + package:darwin: + summary: Packages a production build of the application into a `.app` bundle + platforms: + - darwin + deps: + - task: build:production:darwin + - generate:icons + cmds: + - task: generate:app_bundle + + generate:syso: + dir: build + platforms: + - windows + cmds: + - wails3 generate syso -arch {{.GOARCH}} -icon icon.ico -manifest wails.exe.manifest -info info.json + vars: + GOARCH: '{{.GOARCH}}' + + package:windows: + summary: Packages a production build of the application into a `.exe` bundle + platforms: + - windows/amd64 + deps: + - generate:icons + cmds: + - task: generate:syso + vars: + GOARCH: amd64 + - go build -tags production -gcflags=all="-N -l" -o bin/{{.APP_NAME}} + - powershell Remove-item *.syso + + + package:windows:arm64: + summary: Packages a production build of the application into a `.exe` bundle (ARM64) + platforms: + - windows + cmds: + - task: generate:syso + vars: + GOARCH: arm64 + - go build -tags production -ldflags="-w -s -H windowsgui" -o bin/buildtest.arm64.exe + - powershell Remove-item *.syso + env: + GOARCH: arm64 diff --git a/v3/examples/dialogs/build/Info.dev.plist b/v3/examples/dialogs/build/Info.dev.plist new file mode 100644 index 000000000..d6d28b179 --- /dev/null +++ b/v3/examples/dialogs/build/Info.dev.plist @@ -0,0 +1,35 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My App + CFBundleExecutable + app + CFBundleIdentifier + com.wails.app + CFBundleVersion + v1.0.0 + CFBundleGetInfoString + The ultimate thing + CFBundleShortVersionString + v1 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) Me + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + + + + \ No newline at end of file diff --git a/v3/examples/dialogs/build/Info.plist b/v3/examples/dialogs/build/Info.plist new file mode 100644 index 000000000..7f03f54e9 --- /dev/null +++ b/v3/examples/dialogs/build/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My App + CFBundleExecutable + app + CFBundleIdentifier + com.wails.app + CFBundleVersion + v1.0.0 + CFBundleGetInfoString + The ultimate thing + CFBundleShortVersionString + v1 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + (c) Me + + \ No newline at end of file diff --git a/v3/examples/dialogs/build/appicon.png b/v3/examples/dialogs/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/dialogs/build/appicon.png differ diff --git a/v3/examples/dialogs/build/icon.ico b/v3/examples/dialogs/build/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/dialogs/build/icon.ico differ diff --git a/v3/examples/dialogs/build/icons.icns b/v3/examples/dialogs/build/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/dialogs/build/icons.icns differ diff --git a/v3/examples/dialogs/build/info.json b/v3/examples/dialogs/build/info.json new file mode 100644 index 000000000..1005eb5cb --- /dev/null +++ b/v3/examples/dialogs/build/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "v1.0.0" + }, + "info": { + "0000": { + "ProductVersion": "v1.0.0", + "CompanyName": "My Company Name", + "FileDescription": "A thing that does a thing", + "LegalCopyright": "(c) 2023 My Company Name", + "ProductName": "My Product Name", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/dialogs/build/wails.exe.manifest b/v3/examples/dialogs/build/wails.exe.manifest new file mode 100644 index 000000000..fb1ce5bde --- /dev/null +++ b/v3/examples/dialogs/build/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v3/examples/dialogs/main.go b/v3/examples/dialogs/main.go new file mode 100644 index 000000000..07521b30d --- /dev/null +++ b/v3/examples/dialogs/main.go @@ -0,0 +1,362 @@ +package main + +import ( + _ "embed" + "log" + "log/slog" + "os" + "runtime" + "strings" + + "github.com/wailsapp/wails/v3/pkg/icons" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + + app := application.New(application.Options{ + Name: "Dialogs Demo", + Description: "A demo of the dialogs API", + Assets: application.AlphaAssets, + Logger: application.DefaultLogger(slog.LevelDebug), + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a custom menu + menu := app.NewMenu() + menu.AddRole(application.AppMenu) + menu.AddRole(application.EditMenu) + menu.AddRole(application.WindowMenu) + menu.AddRole(application.ServicesMenu) + menu.AddRole(application.HelpMenu) + + // Let's make a "Demo" menu + infoMenu := menu.AddSubmenu("Info") + infoMenu.Add("Info").OnClick(func(ctx *application.Context) { + dialog := application.InfoDialog() + dialog.SetTitle("Custom Title") + dialog.SetMessage("This is a custom message") + dialog.Show() + }) + + infoMenu.Add("Info (Title only)").OnClick(func(ctx *application.Context) { + dialog := application.InfoDialog() + dialog.SetTitle("Custom Title") + dialog.Show() + }) + infoMenu.Add("Info (Message only)").OnClick(func(ctx *application.Context) { + dialog := application.InfoDialog() + dialog.SetMessage("This is a custom message") + dialog.Show() + }) + infoMenu.Add("Info (Custom Icon)").OnClick(func(ctx *application.Context) { + dialog := application.InfoDialog() + dialog.SetTitle("Custom Icon Example") + dialog.SetMessage("Using a custom icon") + dialog.SetIcon(icons.ApplicationDarkMode256) + dialog.Show() + }) + infoMenu.Add("About").OnClick(func(ctx *application.Context) { + app.Menu.ShowAbout() + }) + + questionMenu := menu.AddSubmenu("Question") + questionMenu.Add("Question (No default)").OnClick(func(ctx *application.Context) { + dialog := application.QuestionDialog() + dialog.SetMessage("No default button") + dialog.AddButton("Yes") + dialog.AddButton("No") + dialog.Show() + }) + questionMenu.Add("Question (Attached to Window)").OnClick(func(ctx *application.Context) { + dialog := application.QuestionDialog() + dialog.AttachToWindow(app.Window.Current()) + dialog.SetMessage("No default button") + dialog.AddButton("Yes") + dialog.AddButton("No") + dialog.Show() + }) + questionMenu.Add("Question (With Default)").OnClick(func(ctx *application.Context) { + dialog := application.QuestionDialog() + dialog.SetTitle("Quit") + dialog.SetMessage("You have unsaved work. Are you sure you want to quit?") + dialog.AddButton("Yes").OnClick(func() { + app.Quit() + }) + no := dialog.AddButton("No") + dialog.SetDefaultButton(no) + dialog.Show() + }) + questionMenu.Add("Question (With Cancel)").OnClick(func(ctx *application.Context) { + dialog := application.QuestionDialog(). + SetTitle("Update"). + SetMessage("The cancel button is selected when pressing escape") + download := dialog.AddButton("📥 Download") + download.OnClick(func() { + application.InfoDialog().SetMessage("Downloading...").Show() + }) + no := dialog.AddButton("Cancel") + dialog.SetDefaultButton(download) + dialog.SetCancelButton(no) + dialog.Show() + }) + questionMenu.Add("Question (Custom Icon)").OnClick(func(ctx *application.Context) { + dialog := application.QuestionDialog() + dialog.SetTitle("Custom Icon Example") + dialog.SetMessage("Using a custom icon") + dialog.SetIcon(icons.WailsLogoWhiteTransparent) + likeIt := dialog.AddButton("I like it!").OnClick(func() { + application.InfoDialog().SetMessage("Thanks!").Show() + }) + dialog.AddButton("Not so keen...").OnClick(func() { + application.InfoDialog().SetMessage("Too bad!").Show() + }) + dialog.SetDefaultButton(likeIt) + dialog.Show() + }) + + warningMenu := menu.AddSubmenu("Warning") + warningMenu.Add("Warning").OnClick(func(ctx *application.Context) { + application.WarningDialog(). + SetTitle("Custom Title"). + SetMessage("This is a custom message"). + Show() + }) + warningMenu.Add("Warning (Title only)").OnClick(func(ctx *application.Context) { + dialog := application.WarningDialog() + dialog.SetTitle("Custom Title") + dialog.Show() + }) + warningMenu.Add("Warning (Message only)").OnClick(func(ctx *application.Context) { + dialog := application.WarningDialog() + dialog.SetMessage("This is a custom message") + dialog.Show() + }) + warningMenu.Add("Warning (Custom Icon)").OnClick(func(ctx *application.Context) { + dialog := application.WarningDialog() + dialog.SetTitle("Custom Icon Example") + dialog.SetMessage("Using a custom icon") + dialog.SetIcon(icons.ApplicationLightMode256) + dialog.Show() + }) + + errorMenu := menu.AddSubmenu("Error") + errorMenu.Add("Error").OnClick(func(ctx *application.Context) { + dialog := application.ErrorDialog() + dialog.SetTitle("Ooops") + dialog.SetMessage("I accidentally the whole of Twitter") + dialog.Show() + }) + errorMenu.Add("Error (Title Only)").OnClick(func(ctx *application.Context) { + dialog := application.ErrorDialog() + dialog.SetTitle("Custom Title") + dialog.Show() + }) + errorMenu.Add("Error (Custom Message)").OnClick(func(ctx *application.Context) { + application.ErrorDialog(). + SetMessage("This is a custom message"). + Show() + }) + errorMenu.Add("Error (Custom Icon)").OnClick(func(ctx *application.Context) { + dialog := application.ErrorDialog() + dialog.SetTitle("Custom Icon Example") + dialog.SetMessage("Using a custom icon") + dialog.SetIcon(icons.WailsLogoWhite) + dialog.Show() + }) + + openMenu := menu.AddSubmenu("Open") + openMenu.Add("Open File").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseFiles(true). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No file selected").Show() + } + }) + openMenu.Add("Open File (Show Hidden Files)").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseFiles(true). + CanCreateDirectories(true). + ShowHiddenFiles(true). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No file selected").Show() + } + }) + openMenu.Add("Open File (Attach to window)").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseFiles(true). + CanCreateDirectories(true). + ShowHiddenFiles(true). + AttachToWindow(app.Window.Current()). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No file selected").Show() + } + }) + openMenu.Add("Open Multiple Files (Show Hidden Files)").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseFiles(true). + CanCreateDirectories(true). + ShowHiddenFiles(true). + PromptForMultipleSelection() + if len(result) > 0 { + application.InfoDialog().SetMessage(strings.Join(result, ",")).Show() + } else { + application.InfoDialog().SetMessage("No file selected").Show() + } + }) + openMenu.Add("Open Directory").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseDirectories(true). + CanChooseFiles(false). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No directory selected").Show() + } + }) + openMenu.Add("Open Directory (Create Directories)").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseDirectories(true). + CanCreateDirectories(true). + CanChooseFiles(false). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No directory selected").Show() + } + }) + openMenu.Add("Open Directory (Resolves Aliases)").OnClick(func(ctx *application.Context) { + result, _ := application.OpenFileDialog(). + CanChooseDirectories(true). + CanCreateDirectories(true). + CanChooseFiles(false). + ResolvesAliases(true). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No directory selected").Show() + } + }) + openMenu.Add("Open File/Directory (Set Title)").OnClick(func(ctx *application.Context) { + dialog := application.OpenFileDialog(). + CanChooseDirectories(true). + CanCreateDirectories(true). + ResolvesAliases(true) + if runtime.GOOS == "darwin" { + dialog.SetMessage("Select a file/directory") + } else { + dialog.SetTitle("Select a file/directory") + } + + result, _ := dialog.PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No file/directory selected").Show() + } + }) + openMenu.Add("Open (Full Example)").OnClick(func(ctx *application.Context) { + cwd, _ := os.Getwd() + dialog := application.OpenFileDialog(). + SetTitle("Select a file"). + SetMessage("Select a file to open"). + SetButtonText("Let's do this!"). + SetDirectory(cwd). + CanCreateDirectories(true). + ResolvesAliases(true). + AllowsOtherFileTypes(true). + TreatsFilePackagesAsDirectories(true). + ShowHiddenFiles(true). + CanSelectHiddenExtension(true). + AddFilter("Text Files", "*.txt; *.md"). + AddFilter("Video Files", "*.mov; *.mp4; *.avi") + + if runtime.GOOS == "darwin" { + dialog.SetMessage("Select a file") + } else { + dialog.SetTitle("Select a file") + } + + result, _ := dialog.PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } else { + application.InfoDialog().SetMessage("No file selected").Show() + } + }) + + saveMenu := menu.AddSubmenu("Save") + saveMenu.Add("Select File (Defaults)").OnClick(func(ctx *application.Context) { + result, _ := application.SaveFileDialog(). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } + }) + saveMenu.Add("Select File (Attach To WebviewWindow)").OnClick(func(ctx *application.Context) { + result, _ := application.SaveFileDialog(). + AttachToWindow(app.Window.Current()). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } + }) + saveMenu.Add("Select File (Show Hidden Files)").OnClick(func(ctx *application.Context) { + result, _ := application.SaveFileDialog(). + ShowHiddenFiles(true). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } + }) + saveMenu.Add("Select File (Cannot Create Directories)").OnClick(func(ctx *application.Context) { + result, _ := application.SaveFileDialog(). + CanCreateDirectories(false). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } + }) + saveMenu.Add("Select File (Full Example)").OnClick(func(ctx *application.Context) { + result, _ := application.SaveFileDialog(). + CanCreateDirectories(false). + ShowHiddenFiles(true). + SetMessage("Select a file"). + SetDirectory("/Applications"). + SetButtonText("Let's do this!"). + SetFilename("README.md"). + HideExtension(true). + AllowsOtherFileTypes(true). + TreatsFilePackagesAsDirectories(true). + ShowHiddenFiles(true). + PromptForSingleSelection() + if result != "" { + application.InfoDialog().SetMessage(result).Show() + } + }) + + app.Menu.Set(menu) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/drag-n-drop/README.md b/v3/examples/drag-n-drop/README.md new file mode 100644 index 000000000..cb3c56fd0 --- /dev/null +++ b/v3/examples/drag-n-drop/README.md @@ -0,0 +1,27 @@ +# Drag-n-drop Example + +This example demonstrates how to handle files being dragged into the application. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run main.go +``` + +## Building the example + +To build the example in debug mode, simply run the following command: + +```bash +wails3 task build +``` + +# Status + +| Platform | Status | +|----------|-------------| +| Mac | Working | +| Windows | Not Working | +| Linux | | diff --git a/v3/examples/drag-n-drop/assets/index.html b/v3/examples/drag-n-drop/assets/index.html new file mode 100644 index 000000000..3d89c9bcb --- /dev/null +++ b/v3/examples/drag-n-drop/assets/index.html @@ -0,0 +1,424 @@ + + + + + + File Tree Drag-and-Drop Example + + + + + + + + + +
+

File Tree Drag & Drop Example

+

Drag files onto folders to upload them to that location

+
+ +
+
+
/home/user
+ + +
+ folder + Home +
+ +
+ +
+ folder + Documents +
+ +
+
+ description + report.pdf +
+
+ description + notes.txt +
+
+ + +
+ folder + Pictures +
+ +
+
+ image + vacation.jpg +
+
+ image + profile.png +
+
+ + +
+ folder + Downloads +
+ +
+
+ archive + app.dmg +
+
+ archive + data.zip +
+
+ + +
+ folder + Projects +
+ +
+ +
+ folder + Wails +
+ +
+
+ code + main.go +
+
+ code + go.mod +
+
+
+
+
+ +
+

Drop Information

+
Drag and drop files onto any folder in the file tree... + +The folder elements have the following attributes: +- data-wails-dropzone: Marks the element as a dropzone +- data-path: Contains the full path (used for destination) +- data-folder-id: A unique ID for the folder +- data-folder-name: The display name of the folder
+
+
+ + + + \ No newline at end of file diff --git a/v3/examples/drag-n-drop/main.go b/v3/examples/drag-n-drop/main.go new file mode 100644 index 000000000..3746a406c --- /dev/null +++ b/v3/examples/drag-n-drop/main.go @@ -0,0 +1,166 @@ +package main + +import ( + "context" + "embed" + _ "embed" + "fmt" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +//go:embed assets +var assets embed.FS + +// App struct +type App struct { + ctx context.Context + app *application.App +} + +// NewApp creates a new App application struct +func NewApp() *App { + return &App{} +} + +// Startup is called when the app starts. The context is saved +// so we can call the runtime methods +func (a *App) Startup(ctx context.Context) { + a.ctx = ctx + a.app = application.Get() +} + +// FileDropInfo defines the payload for the file drop event sent to the frontend. +type FileDropInfo struct { + Files []string `json:"files"` + TargetID string `json:"targetID"` + TargetClasses []string `json:"targetClasses"` + DropX float64 `json:"dropX"` + DropY float64 `json:"dropY"` + Attributes map[string]string `json:"attributes,omitempty"` +} + +// FilesDroppedOnTarget is called when files are dropped onto a registered drop target +// or the window if no specific target is hit. +func FilesDroppedOnTarget( + files []string, + targetID string, + targetClasses []string, + dropX float64, + dropY float64, + isTargetDropzone bool, // This parameter is kept for logging but not sent to frontend in this event + attributes map[string]string, +) { + log.Println("=============== Go: FilesDroppedOnTarget Debug Info ===============") + log.Println(fmt.Sprintf(" Files: %v", files)) + log.Println(fmt.Sprintf(" Target ID: '%s'", targetID)) + log.Println(fmt.Sprintf(" Target Classes: %v", targetClasses)) + log.Println(fmt.Sprintf(" Drop X: %f, Drop Y: %f", dropX, dropY)) + log.Println( + fmt.Sprintf( + " Drop occurred on a designated dropzone (runtime validated before this Go event): %t", + isTargetDropzone, + ), + ) + log.Println(fmt.Sprintf(" Element Attributes: %v", attributes)) + log.Println("================================================================") + + payload := FileDropInfo{ + Files: files, + TargetID: targetID, + TargetClasses: targetClasses, + DropX: dropX, + DropY: dropY, + Attributes: attributes, + } + + log.Println("Go: Emitted 'frontend:FileDropInfo' event with payload:", payload) +} + +func main() { + appInstance := NewApp() + + app := application.New(application.Options{ + Name: "Drag-n-drop Demo", + Description: "A demo of the Drag-n-drop API", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Services: []application.Service{ + application.NewService(appInstance), + }, + }) + + win := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Drag-n-drop Demo", + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + EnableDragAndDrop: true, + }) + + log.Println("Setting up event listener for 'WindowDropZoneFilesDropped'...") + win.OnWindowEvent( + events.Common.WindowDropZoneFilesDropped, + func(event *application.WindowEvent) { + + droppedFiles := event.Context().DroppedFiles() + details := event.Context().DropZoneDetails() + + log.Printf("Dropped files count: %d", len(droppedFiles)) + log.Printf("Event context: %+v", event.Context()) + + if details != nil { + log.Printf("DropZone details found:") + log.Printf(" ElementID: '%s'", details.ElementID) + log.Printf(" ClassList: %v", details.ClassList) + log.Printf(" X: %d, Y: %d", details.X, details.Y) + log.Printf(" Attributes: %+v", details.Attributes) + + // Call the App method with the extracted data + FilesDroppedOnTarget( + droppedFiles, + details.ElementID, + details.ClassList, + float64(details.X), + float64(details.Y), + details.ElementID != "", // isTargetDropzone based on whether an ID was found + details.Attributes, + ) + } else { + log.Println("DropZone details are nil - drop was not on a specific registered zone") + // This case might occur if DropZoneDetails are nil, meaning the drop was not on a specific registered zone + // or if the context itself was problematic. + FilesDroppedOnTarget(droppedFiles, "", nil, 0, 0, false, nil) + } + + payload := FileDropInfo{ + Files: droppedFiles, + TargetID: details.ElementID, + TargetClasses: details.ClassList, + DropX: float64(details.X), + DropY: float64(details.Y), + Attributes: details.Attributes, // Add the attributes + } + + log.Printf("Emitting event payload: %+v", payload) + application.Get().Event.Emit("frontend:FileDropInfo", payload) + log.Println( + "=============== End WindowDropZoneFilesDropped Event Debug ===============", + ) + }, + ) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/environment/README.md b/v3/examples/environment/README.md new file mode 100644 index 000000000..1f922f341 --- /dev/null +++ b/v3/examples/environment/README.md @@ -0,0 +1,19 @@ +# Screen Example + +This example will detect all attached screens and display their details. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/environment/assets/index.html b/v3/examples/environment/assets/index.html new file mode 100644 index 000000000..32bb5ebba --- /dev/null +++ b/v3/examples/environment/assets/index.html @@ -0,0 +1,66 @@ + + + + + Screens Demo + + + + + + + diff --git a/v3/examples/environment/main.go b/v3/examples/environment/main.go new file mode 100644 index 000000000..76822aaa9 --- /dev/null +++ b/v3/examples/environment/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Environment Demo", + Description: "A demo of the Environment API", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Environment Demo", + Width: 800, + Height: 600, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/events-bug/main.go b/v3/examples/events-bug/main.go new file mode 100644 index 000000000..0288243ae --- /dev/null +++ b/v3/examples/events-bug/main.go @@ -0,0 +1,58 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "log" +) + +func main() { + app := application.New(application.Options{ + Name: "Key Bindings Demo", + Description: "A demo of the Key Bindings Options", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + KeyBindings: map[string]func(window application.Window){ + "shift+ctrl+c": func(window application.Window) { + selection, err := application.OpenFileDialog(). + CanChooseFiles(true). + CanCreateDirectories(true). + ShowHiddenFiles(true). + PromptForMultipleSelection() + if err != nil { + println(err.Error()) + } + println(selection) + }, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Name: "Window 1", + Title: "Window 1", + URL: "https://wails.io", + KeyBindings: map[string]func(window application.Window){ + "F12": func(window application.Window) { + window.OpenDevTools() + }, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Name: "Window 2", + Title: "Window 2", + URL: "https://google.com", + KeyBindings: map[string]func(window application.Window){ + "F12": func(window application.Window) { + println("Window 2: Toggle Dev Tools") + }, + }, + }) + + err := app.Run() + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/events/README.md b/v3/examples/events/README.md new file mode 100644 index 000000000..596540f5a --- /dev/null +++ b/v3/examples/events/README.md @@ -0,0 +1,25 @@ +# Events Example + +This example is a demonstration of using the new events API. +It has 2 windows that can emit events from the frontend and the backend emits an event every 10 seconds. +All events emitted are logged either to the console or the window. + +It also demonstrates the use of `RegisterHook` to register a function to be called when an event is emitted. +For one window, it captures the `WindowClosing` event and prevents the window from closing twice. +The other window uses both hooks and events to show the window is gaining focus. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run main.go +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | | +| Windows | Working | +| Linux | | \ No newline at end of file diff --git a/v3/examples/events/assets/index.html b/v3/examples/events/assets/index.html new file mode 100644 index 000000000..069baabdb --- /dev/null +++ b/v3/examples/events/assets/index.html @@ -0,0 +1,29 @@ + + + + + Title + + + +

Events Demo

+
+The main program emits an event every 10s which will be displayed in the section below. +To send an event from this window, click here: +
+ + + + + diff --git a/v3/examples/events/main.go b/v3/examples/events/main.go new file mode 100644 index 000000000..6f3d71be5 --- /dev/null +++ b/v3/examples/events/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "embed" + _ "embed" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" +) + +//go:embed assets +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "customEventProcessor Demo", + Description: "A demo of the customEventProcessor API", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Custom event handling + app.Event.On("myevent", func(e *application.CustomEvent) { + app.Logger.Info("[Go] CustomEvent received", "name", e.Name, "data", e.Data, "sender", e.Sender, "cancelled", e.IsCancelled()) + }) + + // OS specific application events + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) { + go func() { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + // This emits a custom event every 10 seconds + // As it's sent from the application, the sender will be blank + app.Event.Emit("myevent", "hello") + case <-app.Context().Done(): + return + } + } + }() + }) + + app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) { + app.Logger.Info("System theme changed!") + if event.Context().IsDarkMode() { + app.Logger.Info("System is now using dark mode!") + } else { + app.Logger.Info("System is now using light mode!") + } + }) + + // Platform agnostic events + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) { + app.Logger.Info("events.Common.ApplicationStarted fired!") + }) + + win1 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Name: "Window 1", + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + var countdown = 3 + + win1.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + countdown-- + if countdown == 0 { + app.Logger.Info("Window 1 Closing!") + return + } + app.Logger.Info("Window 1 Closing? Nope! Not closing!") + e.Cancel() + }) + + win2 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 2", + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + go func() { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + win2.EmitEvent("windowevent", "ooooh!") + case <-app.Context().Done(): + return + } + } + }() + + var cancel bool + + win2.RegisterHook(events.Common.WindowFocus, func(e *application.WindowEvent) { + app.Logger.Info("[Hook] Window focus!") + cancel = !cancel + if cancel { + e.Cancel() + } + }) + + win2.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { + app.Logger.Info("[OnWindowEvent] Window focus!") + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/file-association/.gitignore b/v3/examples/file-association/.gitignore new file mode 100644 index 000000000..4b51c175c --- /dev/null +++ b/v3/examples/file-association/.gitignore @@ -0,0 +1,3 @@ +.task +bin +wails.syso \ No newline at end of file diff --git a/v3/examples/file-association/Inter Font License.txt b/v3/examples/file-association/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/examples/file-association/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/examples/file-association/README.md b/v3/examples/file-association/README.md new file mode 100644 index 000000000..0aa08023f --- /dev/null +++ b/v3/examples/file-association/README.md @@ -0,0 +1,11 @@ +# File Association Sample Project + +This sample project demonstrates how to associate a file type with an application. +More info at: https://v3.wails.io/learn/guides/file-associations/ + +To run the sample, follow these steps: + +1. Run `wails3 package` to generate the package. +2. On Windows, run the installer that was built in the `bin` directory. +3. Double-click on the `test.wails` file to open it with the application. +4. On macOS, double-click on the `test.wails` file and select the built application. \ No newline at end of file diff --git a/v3/examples/file-association/Taskfile.yml b/v3/examples/file-association/Taskfile.yml new file mode 100644 index 000000000..4ec68e23f --- /dev/null +++ b/v3/examples/file-association/Taskfile.yml @@ -0,0 +1,54 @@ +version: '3' + +includes: + common: ./build/Taskfile.common.yml + windows: ./build/Taskfile.windows.yml + darwin: ./build/Taskfile.darwin.yml + linux: ./build/Taskfile.linux.yml + +vars: + APP_NAME: "fileassoc" + BIN_DIR: "bin" + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + + darwin:build:universal: + summary: Builds darwin universal binary (arm64 + amd64) + cmds: + - task: darwin:build + vars: + ARCH: amd64 + - mv {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}-amd64 + - task: darwin:build + vars: + ARCH: arm64 + - mv {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}-arm64 + - 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 + + darwin:package:universal: + summary: Packages darwin universal binary (arm64 + amd64) + deps: + - darwin:build:universal + cmds: + - task: darwin:create:app:bundle diff --git a/v3/examples/file-association/build/Info.dev.plist b/v3/examples/file-association/build/Info.dev.plist new file mode 100644 index 000000000..327c94603 --- /dev/null +++ b/v3/examples/file-association/build/Info.dev.plist @@ -0,0 +1,32 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + fileassoc + CFBundleIdentifier + + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/v3/examples/file-association/build/Info.plist b/v3/examples/file-association/build/Info.plist new file mode 100644 index 000000000..1b3520754 --- /dev/null +++ b/v3/examples/file-association/build/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + fileassoc + CFBundleIdentifier + + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + + \ No newline at end of file diff --git a/v3/examples/file-association/build/Taskfile.common.yml b/v3/examples/file-association/build/Taskfile.common.yml new file mode 100644 index 000000000..650c8ea83 --- /dev/null +++ b/v3/examples/file-association/build/Taskfile.common.yml @@ -0,0 +1,75 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + generates: + - go.sum + sources: + - go.mod + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + 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 + + build:frontend: + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/* + deps: + - task: install:frontend:deps + - task: generate:bindings + cmds: + - npm run build -q + + generate:bindings: + summary: Generates bindings for the frontend + sources: + - "**/*.go" + - go.mod + - go.sum + generates: + - "frontend/bindings/**/*" + cmds: + - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true {{if .UseTypescript}} -ts{{end}} + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + sources: + - "appicon.png" + generates: + - "icons.icns" + - "icon.ico" + cmds: + - wails3 generate icons -input appicon.png + + dev:frontend: + summary: Runs the frontend in development mode + dir: frontend + deps: + - task: install:frontend:deps + cmds: + - npm 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 . \ No newline at end of file diff --git a/v3/examples/file-association/build/Taskfile.darwin.yml b/v3/examples/file-association/build/Taskfile.darwin.yml new file mode 100644 index 000000000..45db6d067 --- /dev/null +++ b/v3/examples/file-association/build/Taskfile.darwin.yml @@ -0,0 +1,45 @@ +version: '3' + +includes: + common: Taskfile.common.yml + +tasks: + build: + summary: Creates a production build of the application + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -ldflags="-w -s"{{else}}-gcflags=all="-l"{{end}}' + env: + GOOS: darwin + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + CGO_CFLAGS: "-mmacosx-version-min=10.15" + CGO_LDFLAGS: "-mmacosx-version-min=10.15" + MACOSX_DEPLOYMENT_TARGET: "10.15" + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application into a `.app` bundle + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: create:app:bundle + + create:app:bundle: + summary: Creates an `.app` bundle + cmds: + - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/{MacOS,Resources} + - cp build/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS + - cp build/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/file-association/build/Taskfile.linux.yml b/v3/examples/file-association/build/Taskfile.linux.yml new file mode 100644 index 000000000..814ee0ae1 --- /dev/null +++ b/v3/examples/file-association/build/Taskfile.linux.yml @@ -0,0 +1,66 @@ +version: '3' + +includes: + common: Taskfile.common.yml + +tasks: + build: + summary: Builds the application for Linux + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -ldflags="-w -s"{{else}}-gcflags=all="-l"{{end}}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application for Linux + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: create:appimage + + create:appimage: + summary: Creates an AppImage + dir: build/appimage + deps: + - task: build + vars: + PRODUCTION: "true" + - task: generate:dotdesktop + cmds: + - cp {{.APP_BINARY}} {{.APP_NAME}} + - cp ../appicon.png appicon.png + - wails3 generate appimage -binary {{.APP_NAME}} -icon {{.ICON}} -desktopfile {{.DESKTOP_FILE}} -outputdir {{.OUTPUT_DIR}} -builddir {{.ROOT_DIR}}/build/appimage + vars: + APP_NAME: '{{.APP_NAME}}' + APP_BINARY: '../../bin/{{.APP_NAME}}' + ICON: '../appicon.png' + DESKTOP_FILE: '{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../bin' + + generate:dotdesktop: + summary: Generates a `.desktop` file + dir: build + cmds: + - mkdir -p {{.ROOT_DIR}}/build/appimage + - wails3 generate .desktop -name "{{.APP_NAME}}" -exec "{{.EXEC}}" -icon "{{.ICON}}" -outputfile {{.ROOT_DIR}}/build/appimage/{{.APP_NAME}}.desktop -categories "{{.CATEGORIES}}" + vars: + APP_NAME: '{{.APP_NAME}}' + EXEC: '{{.APP_NAME}}' + ICON: 'appicon' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/appimage/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/file-association/build/Taskfile.windows.yml b/v3/examples/file-association/build/Taskfile.windows.yml new file mode 100644 index 000000000..f141fcd2f --- /dev/null +++ b/v3/examples/file-association/build/Taskfile.windows.yml @@ -0,0 +1,53 @@ +version: '3' + +includes: + common: Taskfile.common.yml + +tasks: + build: + summary: Builds the application for Windows + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + - task: common:generate:icons + - task: generate:syso + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}.exe + - powershell Remove-item *.syso + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -ldflags="-w -s -H windowsgui"{{else}}-gcflags=all="-l"{{end}}' + env: + GOOS: windows + CGO_ENABLED: 0 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application into a `.exe` bundle + cmds: + - task: create:nsis:installer + + generate:syso: + summary: Generates Windows `.syso` file + dir: build + cmds: + - wails3 generate syso -arch {{.ARCH}} -icon icon.ico -manifest wails.exe.manifest -info info.json + vars: + ARCH: '{{.ARCH | default ARCH}}' + + create:nsis:installer: + summary: Creates an NSIS installer + dir: build/nsis + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}\{{.BIN_DIR}}\{{.APP_NAME}}.exe" project.nsi + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + + run: + cmds: + - '{{.BIN_DIR}}\\{{.APP_NAME}}.exe' \ No newline at end of file diff --git a/v3/examples/file-association/build/appicon.png b/v3/examples/file-association/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/file-association/build/appicon.png differ diff --git a/v3/examples/file-association/build/appimage/build.sh b/v3/examples/file-association/build/appimage/build.sh new file mode 100644 index 000000000..fcba535e5 --- /dev/null +++ b/v3/examples/file-association/build/appimage/build.sh @@ -0,0 +1,26 @@ +#!/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}/" + +# 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 + +# Rename the generated AppImage +mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage" + diff --git a/v3/examples/file-association/build/config.yml b/v3/examples/file-association/build/config.yml new file mode 100644 index 000000000..0786788ae --- /dev/null +++ b/v3/examples/file-association/build/config.yml @@ -0,0 +1,32 @@ +# 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: "My Company" # The name of the company + productName: "My Product" # The name of the application + productIdentifier: "com.mycompany.myproduct" # The unique product identifier + description: "A program that does X" # The application description + copyright: "(c) 2025, My Company" # Copyright text + comments: "Some Product Comments" # Comments + version: "v0.0.1" # The application version + +# File Associations +# More information at: https://v3.wails.io/noit/done/yet +fileAssociations: + - ext: wails + name: Wails + description: Wails Application File + iconName: icon + role: Editor +# - ext: jpg +# name: JPEG +# description: Image File +# iconName: jpegFileIcon +# role: Editor + +# Other data +other: + - name: My Other Data \ No newline at end of file diff --git a/v3/examples/file-association/build/devmode.config.yaml b/v3/examples/file-association/build/devmode.config.yaml new file mode 100644 index 000000000..7d674a261 --- /dev/null +++ b/v3/examples/file-association/build/devmode.config.yaml @@ -0,0 +1,28 @@ +config: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - cmd: wails3 task run + type: primary diff --git a/v3/examples/file-association/build/icon.ico b/v3/examples/file-association/build/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/file-association/build/icon.ico differ diff --git a/v3/examples/file-association/build/icons.icns b/v3/examples/file-association/build/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/file-association/build/icons.icns differ diff --git a/v3/examples/file-association/build/info.json b/v3/examples/file-association/build/info.json new file mode 100644 index 000000000..850b2b5b0 --- /dev/null +++ b/v3/examples/file-association/build/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.1.0" + }, + "info": { + "0000": { + "ProductVersion": "0.1.0", + "CompanyName": "My Company", + "FileDescription": "My Product Description", + "LegalCopyright": "© now, My Company", + "ProductName": "My Product", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/file-association/build/nsis/project.nsi b/v3/examples/file-association/build/nsis/project.nsi new file mode 100644 index 000000000..11ebc1ec7 --- /dev/null +++ b/v3/examples/file-association/build/nsis/project.nsi @@ -0,0 +1,112 @@ +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 "fileassoc" +## !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 "© now, 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 +#### +## 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. +InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). +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.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.deleteUninstaller +SectionEnd diff --git a/v3/examples/file-association/build/nsis/wails_tools.nsh b/v3/examples/file-association/build/nsis/wails_tools.nsh new file mode 100644 index 000000000..d49f6c803 --- /dev/null +++ b/v3/examples/file-association/build/nsis/wails_tools.nsh @@ -0,0 +1,218 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "fileassoc" +!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 "© now, 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 REQUEST_EXECUTION_LEVEL + !define REQUEST_EXECUTION_LEVEL "admin" +!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 + 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" +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + DeleteRegKey HKLM "${UNINST_KEY}" +!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 + + !insertmacro APP_ASSOCIATE "wails" "Wails" "Wails Application File" "$INSTDIR\icon.ico" "Open with ${INFO_PRODUCTNAME}" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\"" + File "..\icon.ico" + +!macroend + +!macro wails.unassociateFiles + ; Delete app associations + + !insertmacro APP_UNASSOCIATE "wails" "Wails" + Delete "$INSTDIR\icon.ico" + +!macroend \ No newline at end of file diff --git a/v3/examples/file-association/build/wails.exe.manifest b/v3/examples/file-association/build/wails.exe.manifest new file mode 100644 index 000000000..03a121e40 --- /dev/null +++ b/v3/examples/file-association/build/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v3/examples/file-association/frontend/bindings/changeme/greetservice.js b/v3/examples/file-association/frontend/bindings/changeme/greetservice.js new file mode 100644 index 000000000..860020abd --- /dev/null +++ b/v3/examples/file-association/frontend/bindings/changeme/greetservice.js @@ -0,0 +1,16 @@ +// @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 {Call as $Call, Create as $Create} from "@wailsio/runtime"; + +/** + * @param {string} name + * @returns {Promise & { cancel(): void }} + */ +export function Greet(name) { + let $resultPromise = /** @type {any} */($Call.ByID(1411160069, name)); + return $resultPromise; +} diff --git a/v3/examples/file-association/frontend/bindings/changeme/index.js b/v3/examples/file-association/frontend/bindings/changeme/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/examples/file-association/frontend/bindings/changeme/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/examples/file-association/frontend/index.html b/v3/examples/file-association/frontend/index.html new file mode 100644 index 000000000..b81d9729f --- /dev/null +++ b/v3/examples/file-association/frontend/index.html @@ -0,0 +1,35 @@ + + + + + + + + Wails App + + +
+ +

Wails + Javascript

+
+
Please enter your name below 👇
+
+ + +
+
+ +
+ + + diff --git a/v3/examples/file-association/frontend/main.js b/v3/examples/file-association/frontend/main.js new file mode 100644 index 000000000..c24b3b1ef --- /dev/null +++ b/v3/examples/file-association/frontend/main.js @@ -0,0 +1,21 @@ +import {GreetService} from "./bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +const resultElement = document.getElementById('result'); +const timeElement = document.getElementById('time'); + +window.doGreet = () => { + let name = document.getElementById('name').value; + if (!name) { + name = 'anonymous'; + } + GreetService.Greet(name).then((result) => { + resultElement.innerText = result; + }).catch((err) => { + console.log(err); + }); +} + +Events.On('time', (time) => { + timeElement.innerText = time.data; +}); diff --git a/v3/examples/file-association/frontend/package-lock.json b/v3/examples/file-association/frontend/package-lock.json new file mode 100644 index 000000000..9ba47fffa --- /dev/null +++ b/v3/examples/file-association/frontend/package-lock.json @@ -0,0 +1,860 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "devDependencies": { + "@wailsio/runtime": "latest", + "vite": "^5.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz", + "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz", + "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz", + "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz", + "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz", + "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz", + "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz", + "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz", + "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz", + "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz", + "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz", + "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz", + "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz", + "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz", + "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz", + "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz", + "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz", + "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz", + "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@wailsio/runtime": { + "version": "3.0.0-alpha.28", + "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.28.tgz", + "integrity": "sha512-caMnAcKxxDrIWYgCZAMY2kdL++X4ehO2+JvH5na21xfDqz3VnHkEjxsH3jfhgd34M8LY80QEH8iqoMYytDFE/g==", + "dev": true, + "dependencies": { + "nanoid": "^5.0.7" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.8.tgz", + "integrity": "sha512-TcJPw+9RV9dibz1hHUzlLVy8N4X9TnwirAjrU08Juo6BNKggzVfP2ZJ/3ZUSq15Xl5i85i+Z89XBO90pB2PghQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/rollup": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz", + "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.3", + "@rollup/rollup-android-arm64": "4.24.3", + "@rollup/rollup-darwin-arm64": "4.24.3", + "@rollup/rollup-darwin-x64": "4.24.3", + "@rollup/rollup-freebsd-arm64": "4.24.3", + "@rollup/rollup-freebsd-x64": "4.24.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.3", + "@rollup/rollup-linux-arm-musleabihf": "4.24.3", + "@rollup/rollup-linux-arm64-gnu": "4.24.3", + "@rollup/rollup-linux-arm64-musl": "4.24.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3", + "@rollup/rollup-linux-riscv64-gnu": "4.24.3", + "@rollup/rollup-linux-s390x-gnu": "4.24.3", + "@rollup/rollup-linux-x64-gnu": "4.24.3", + "@rollup/rollup-linux-x64-musl": "4.24.3", + "@rollup/rollup-win32-arm64-msvc": "4.24.3", + "@rollup/rollup-win32-ia32-msvc": "4.24.3", + "@rollup/rollup-win32-x64-msvc": "4.24.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.4.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", + "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/v3/examples/file-association/frontend/package.json b/v3/examples/file-association/frontend/package.json new file mode 100644 index 000000000..2642d7a41 --- /dev/null +++ b/v3/examples/file-association/frontend/package.json @@ -0,0 +1,16 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build --minify false --mode development", + "build:prod": "vite build --mode production", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^5.0.0", + "@wailsio/runtime": "latest" + } +} \ No newline at end of file diff --git a/v3/examples/file-association/frontend/public/Inter-Medium.ttf b/v3/examples/file-association/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/file-association/frontend/public/Inter-Medium.ttf differ diff --git a/v3/examples/file-association/frontend/public/javascript.svg b/v3/examples/file-association/frontend/public/javascript.svg new file mode 100644 index 000000000..f9abb2b72 --- /dev/null +++ b/v3/examples/file-association/frontend/public/javascript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/file-association/frontend/public/style.css b/v3/examples/file-association/frontend/public/style.css new file mode 100644 index 000000000..259397254 --- /dev/null +++ b/v3/examples/file-association/frontend/public/style.css @@ -0,0 +1,160 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/file-association/frontend/public/wails.png b/v3/examples/file-association/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/file-association/frontend/public/wails.png differ diff --git a/v3/examples/file-association/go.mod b/v3/examples/file-association/go.mod new file mode 100644 index 000000000..4d7fef32e --- /dev/null +++ b/v3/examples/file-association/go.mod @@ -0,0 +1,50 @@ +module changeme + +go 1.24.0 + +require github.com/wailsapp/wails/v3 v3.0.0-alpha.7 + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) + +replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/file-association/go.sum b/v3/examples/file-association/go.sum new file mode 100644 index 000000000..cbadfe003 --- /dev/null +++ b/v3/examples/file-association/go.sum @@ -0,0 +1,146 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/v3/examples/file-association/greetservice.go b/v3/examples/file-association/greetservice.go new file mode 100644 index 000000000..8972c39cd --- /dev/null +++ b/v3/examples/file-association/greetservice.go @@ -0,0 +1,7 @@ +package main + +type GreetService struct{} + +func (g *GreetService) Greet(name string) string { + return "Hello " + name + "!" +} diff --git a/v3/examples/file-association/main.go b/v3/examples/file-association/main.go new file mode 100644 index 000000000..9fb6fa058 --- /dev/null +++ b/v3/examples/file-association/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "embed" + _ "embed" + "github.com/wailsapp/wails/v3/pkg/events" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed frontend +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + app := application.New(application.Options{ + Name: "fileassoc", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + FileAssociations: []string{".wails"}, + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + var filename string + app.Event.OnApplicationEvent(events.Common.ApplicationOpenedWithFile, func(event *application.ApplicationEvent) { + filename = event.Context().Filename() + }) + + window.OnWindowEvent(events.Common.WindowShow, func(event *application.WindowEvent) { + application.InfoDialog(). + SetTitle("File Opened"). + SetMessage("Application opened with file: " + filename). + Show() + }) + + // Create a goroutine that emits an event containing the current time every second. + // The frontend can listen to this event and update the UI accordingly. + go func() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + now := time.Now().Format(time.RFC1123) + app.Event.Emit("time", now) + case <-app.Context().Done(): + return + } + } + }() + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/file-association/test.wails b/v3/examples/file-association/test.wails new file mode 100644 index 000000000..dde58d5e8 --- /dev/null +++ b/v3/examples/file-association/test.wails @@ -0,0 +1 @@ +Once the application is built and installed, double click on this file to open the application. \ No newline at end of file diff --git a/v3/examples/frameless/README.md b/v3/examples/frameless/README.md new file mode 100644 index 000000000..f17f7a5d3 --- /dev/null +++ b/v3/examples/frameless/README.md @@ -0,0 +1,19 @@ +# Frameless Example + +This example is a demonstration of using frameless windows in Wails. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/frameless/assets/index.html b/v3/examples/frameless/assets/index.html new file mode 100644 index 000000000..2776cfaa9 --- /dev/null +++ b/v3/examples/frameless/assets/index.html @@ -0,0 +1,53 @@ + + + + + + + + +
+
Draggable
+
Not Draggable
+
Not Draggable
+
Not Draggable
+
Draggable
+
+ + diff --git a/v3/examples/frameless/main.go b/v3/examples/frameless/main.go new file mode 100644 index 000000000..93be412fd --- /dev/null +++ b/v3/examples/frameless/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Frameless Demo", + Description: "A demo of frameless windows", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Frameless: true, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/gin-example/README.md b/v3/examples/gin-example/README.md new file mode 100644 index 000000000..b4d85cb9b --- /dev/null +++ b/v3/examples/gin-example/README.md @@ -0,0 +1,104 @@ +# Gin Example + +This example demonstrates how to use the [Gin web framework](https://github.com/gin-gonic/gin) with Wails. + +## Overview + +This example shows how to: + +- Set up Gin as the asset handler for a Wails application +- Create a middleware that routes requests between Wails and Gin +- Define API endpoints with Gin +- Communicate between the Gin-served frontend and Wails backend +- Implement custom Gin middleware + +## Running the Example + +```bash +cd v3/examples/gin-example +go mod tidy +go run . +``` + +## How It Works + +The example uses Gin's HTTP router to serve the frontend content whilst still allowing Wails to handle its internal routes. This is achieved through: + +1. Creating a Gin router with routes for the frontend +2. Implementing a middleware function that decides whether to pass requests to Gin or let Wails handle them +3. Configuring the Wails application to use both the Gin router as the asset handler and the custom middleware + +### Wails-Gin Integration + +The key part of the integration is the middleware function: + +```go +func GinMiddleware(ginEngine *gin.Engine) application.Middleware { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Let Wails handle its internal routes + if r.URL.Path == "/wails/runtime.js" || r.URL.Path == "/wails/ipc" { + next.ServeHTTP(w, r) + return + } + + // Let Gin handle everything else + ginEngine.ServeHTTP(w, r) + }) + } +} +``` + +This allows you to leverage Gin's powerful routing and middleware capabilities whilst still maintaining full access to Wails features. + +### Custom Gin Middleware + +The example also demonstrates how to create custom Gin middleware: + +```go +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Start timer + startTime := time.Now() + + // Process request + c.Next() + + // Calculate latency + latency := time.Since(startTime) + + // Log request details + log.Printf("[GIN] %s | %s | %s | %d | %s", + c.Request.Method, + c.Request.URL.Path, + c.ClientIP(), + c.Writer.Status(), + latency, + ) + } +} +``` + +This middleware is applied to all Gin routes and logs details about each request. + +### Application Configuration + +The Wails application is configured to use Gin as follows: + +```go +app := application.New(application.Options{ + Name: "Gin Example", + Description: "A demo of using Gin with Wails", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: ginEngine, + Middleware: GinMiddleware(ginEngine), + }, +}) +``` + +This configuration tells Wails to: +1. Use the Gin engine as the primary handler for HTTP requests +2. Use our custom middleware to route requests between Wails and Gin diff --git a/v3/examples/gin-example/go.mod b/v3/examples/gin-example/go.mod new file mode 100644 index 000000000..ef253a93c --- /dev/null +++ b/v3/examples/gin-example/go.mod @@ -0,0 +1,72 @@ +module gin-example + +go 1.24.0 + +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/wailsapp/wails/v3 v3.0.0 +) + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/gin-example/go.sum b/v3/examples/gin-example/go.sum new file mode 100644 index 000000000..59438b668 --- /dev/null +++ b/v3/examples/gin-example/go.sum @@ -0,0 +1,203 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/v3/examples/gin-example/main.go b/v3/examples/gin-example/main.go new file mode 100644 index 000000000..8138d5403 --- /dev/null +++ b/v3/examples/gin-example/main.go @@ -0,0 +1,116 @@ +package main + +import ( + "embed" + "log" + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed static +var staticFiles embed.FS + +// GinMiddleware creates a middleware that passes requests to Gin if they're not handled by Wails +func GinMiddleware(ginEngine *gin.Engine) application.Middleware { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Let Wails handle the `/wails` route + if strings.HasPrefix(r.URL.Path, "/wails") { + next.ServeHTTP(w, r) + return + } + // Let Gin handle everything else + ginEngine.ServeHTTP(w, r) + }) + } +} + +// LoggingMiddleware is a Gin middleware that logs request details +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Start timer + startTime := time.Now() + + // Process request + c.Next() + + // Calculate latency + latency := time.Since(startTime) + + // Log request details + log.Printf("[GIN] %s | %s | %s | %d | %s", + c.Request.Method, + c.Request.URL.Path, + c.ClientIP(), + c.Writer.Status(), + latency, + ) + } +} + +func main() { + // Create a new Gin router + ginEngine := gin.New() // Using New() instead of Default() to add our own middleware + + // Add middlewares + ginEngine.Use(gin.Recovery()) + ginEngine.Use(LoggingMiddleware()) + + // Serve embedded static files + ginEngine.StaticFS("/static", http.FS(staticFiles)) + + // Define routes + ginEngine.GET("/", func(c *gin.Context) { + file, err := staticFiles.ReadFile("static/index.html") + if err != nil { + c.String(http.StatusInternalServerError, "Error reading index.html") + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", file) + }) + + ginEngine.GET("/api/hello", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "Hello from Gin API!", + "time": time.Now().Format(time.RFC3339), + }) + }) + + // Create a new Wails application + app := application.New(application.Options{ + Name: "Gin Example", + Description: "A demo of using Gin with Wails", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: ginEngine, + Middleware: GinMiddleware(ginEngine), + }, + }) + + // Register event handler and store cleanup function + removeGinHandler := app.Event.On("gin-button-clicked", func(event *application.CustomEvent) { + log.Printf("Received event from frontend: %v", event.Data) + }) + // Note: In production, call removeGinHandler() during cleanup + _ = removeGinHandler + + // Create window + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Wails + Gin Example", + Width: 900, + Height: 700, + URL: "/", + }) + + // Run the app + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/gin-example/static/index.html b/v3/examples/gin-example/static/index.html new file mode 100644 index 000000000..df02b840f --- /dev/null +++ b/v3/examples/gin-example/static/index.html @@ -0,0 +1,96 @@ + + + Wails + Gin Example + + + +
+

Wails + Gin Integration

+
+

Hello World!

+

This page is being served by Gin.

+

Click the button below to trigger a Wails event:

+ + +
+
+

API Example

+

Try the Gin API endpoint:

+ +
+
+
+ + + diff --git a/v3/examples/gin-routing/README.md b/v3/examples/gin-routing/README.md new file mode 100644 index 000000000..5d8babc31 --- /dev/null +++ b/v3/examples/gin-routing/README.md @@ -0,0 +1,26 @@ +# Gin Routing Example + +This example demonstrates how to use the [Gin web framework](https://github.com/gin-gonic/gin) as a router with Wails. + +## Overview + +This example shows how to: + +- Set up Gin as the asset handler for a Wails application +- Create a middleware that routes requests between Wails and Gin +- Define API endpoints with Gin +- Communicate between the Gin-served frontend and Wails backend +- Implement custom Gin middleware + +## Running the Example + +```bash +cd v3/examples/gin-routing +go mod tidy +go run . +``` + +## Documentation + +Please consult the [Using Gin for Routing](https://v3.wails.io/guides/gin-routing/) guide for a detailed explanation of the example. + diff --git a/v3/examples/gin-routing/go.mod b/v3/examples/gin-routing/go.mod new file mode 100644 index 000000000..ef253a93c --- /dev/null +++ b/v3/examples/gin-routing/go.mod @@ -0,0 +1,72 @@ +module gin-example + +go 1.24.0 + +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/wailsapp/wails/v3 v3.0.0 +) + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/gin-routing/go.sum b/v3/examples/gin-routing/go.sum new file mode 100644 index 000000000..59438b668 --- /dev/null +++ b/v3/examples/gin-routing/go.sum @@ -0,0 +1,203 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/v3/examples/gin-routing/main.go b/v3/examples/gin-routing/main.go new file mode 100644 index 000000000..a78494808 --- /dev/null +++ b/v3/examples/gin-routing/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "embed" + "log" + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed static +var staticFiles embed.FS + +// GinMiddleware creates a middleware that passes requests to Gin if they're not handled by Wails +func GinMiddleware(ginEngine *gin.Engine) application.Middleware { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Let Wails handle the `/wails` route + if strings.HasPrefix(r.URL.Path, "/wails") { + next.ServeHTTP(w, r) + return + } + // Let Gin handle everything else + ginEngine.ServeHTTP(w, r) + }) + } +} + +// LoggingMiddleware is a Gin middleware that logs request details +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Start timer + startTime := time.Now() + + // Process request + c.Next() + + // Calculate latency + latency := time.Since(startTime) + + // Log request details + log.Printf("[GIN] %s | %s | %s | %d | %s", + c.Request.Method, + c.Request.URL.Path, + c.ClientIP(), + c.Writer.Status(), + latency, + ) + } +} + +func main() { + // Create a new Gin router + ginEngine := gin.New() // Using New() instead of Default() to add our own middleware + + // Add middlewares + ginEngine.Use(gin.Recovery()) + ginEngine.Use(LoggingMiddleware()) + + // Serve embedded static files + ginEngine.StaticFS("/static", http.FS(staticFiles)) + + // Define routes + ginEngine.GET("/", func(c *gin.Context) { + file, err := staticFiles.ReadFile("static/index.html") + if err != nil { + c.String(http.StatusInternalServerError, "Error reading index.html") + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", file) + }) + + ginEngine.GET("/api/hello", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "Hello from Gin API!", + "time": time.Now().Format(time.RFC3339), + }) + }) + + // Create a new Wails application + app := application.New(application.Options{ + Name: "Gin Example", + Description: "A demo of using Gin with Wails", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: ginEngine, + Middleware: GinMiddleware(ginEngine), + }, + }) + + // Register event handler + app.Event.On("gin-button-clicked", func(event *application.CustomEvent) { + log.Printf("Received event from frontend: %v", event.Data) + }) + + // Create window + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Wails + Gin Example", + Width: 900, + Height: 700, + URL: "/", + }) + + // Run the app + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/gin-routing/static/index.html b/v3/examples/gin-routing/static/index.html new file mode 100644 index 000000000..aefbd7a28 --- /dev/null +++ b/v3/examples/gin-routing/static/index.html @@ -0,0 +1,94 @@ + + + Wails + Gin Example + + + +
+

Wails + Gin Integration

+
+

Hello World!

+

This page is being served by Gin.

+

Click the button below to trigger a Wails event:

+ + +
+
+

API Example

+

Try the Gin API endpoint:

+ +
+
+
+ + + diff --git a/v3/examples/gin-service/README.md b/v3/examples/gin-service/README.md new file mode 100644 index 000000000..89e5019d7 --- /dev/null +++ b/v3/examples/gin-service/README.md @@ -0,0 +1,21 @@ +# Gin Service Example + +This example demonstrates how to use the [Gin web framework](https://github.com/gin-gonic/gin) in a Wails Service. + +## Overview + +This example shows how to: + +- Set up Gin as the asset handler for a Wails application +- Create a middleware that routes requests between Wails and Gin +- Define API endpoints with Gin +- Communicate between the Gin-served frontend and Wails backend +- Implement custom Gin middleware + +## Running the Example + + +## Documentation + +Please consult the [Using Gin for Routing](https://v3.wails.io/guides/gin-routing/) guide for a detailed explanation of the example. + diff --git a/v3/examples/gin-service/assets/index.html b/v3/examples/gin-service/assets/index.html new file mode 100644 index 000000000..34324b89f --- /dev/null +++ b/v3/examples/gin-service/assets/index.html @@ -0,0 +1,249 @@ + + + + + + Gin Service Example + + + +

Gin Service Example

+ +
+

API Endpoints

+

Try the Gin API endpoints mounted at /api:

+ + + + + + + +
+
Results will appear here...
+
+
+ +
+

Event Communication

+

Trigger an event to communicate with the Gin service:

+ + + +
+
Event responses will appear here...
+
+
+ + + + + + diff --git a/v3/examples/gin-service/go.mod b/v3/examples/gin-service/go.mod new file mode 100644 index 000000000..607c1701f --- /dev/null +++ b/v3/examples/gin-service/go.mod @@ -0,0 +1,74 @@ +module gin-service + +go 1.24.0 + +require ( + github.com/gin-gonic/gin v1.10.0 + github.com/wailsapp/wails/v3 v3.0.0-alpha.9 +) + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/wailsapp/wails/v3 v3.0.0-alpha.9 => ../.. diff --git a/v3/examples/gin-service/go.sum b/v3/examples/gin-service/go.sum new file mode 100644 index 000000000..86b344c16 --- /dev/null +++ b/v3/examples/gin-service/go.sum @@ -0,0 +1,208 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/v3/examples/gin-service/main.go b/v3/examples/gin-service/main.go new file mode 100644 index 000000000..f27fef415 --- /dev/null +++ b/v3/examples/gin-service/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "embed" + "log/slog" + "os" + + "gin-service/services" + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + app := application.New(application.Options{ + Name: "Gin Service Demo", + Description: "A demo of using Gin in Wails services", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + LogLevel: slog.LevelDebug, + Services: []application.Service{ + application.NewServiceWithOptions(services.NewGinService(), application.ServiceOptions{ + Route: "/api", + }), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Gin Service Demo", + Width: 1024, + Height: 768, + }) + + err := app.Run() + + if err != nil { + println(err.Error()) + os.Exit(1) + } +} diff --git a/v3/examples/gin-service/services/gin_service.go b/v3/examples/gin-service/services/gin_service.go new file mode 100644 index 000000000..e0fa290ec --- /dev/null +++ b/v3/examples/gin-service/services/gin_service.go @@ -0,0 +1,250 @@ +package services + +import ( + "context" + "net/http" + "strconv" + "strings" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/wailsapp/wails/v3/pkg/application" +) + +// User represents a user in the system +type User struct { + ID int `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + CreatedAt time.Time `json:"createdAt"` +} + +// GinService implements a Wails service that uses Gin for HTTP handling +type GinService struct { + ginEngine *gin.Engine + users []User + nextID int + mu sync.RWMutex + app *application.App + maxUsers int // Maximum number of users to prevent unbounded growth + removeEventHandler func() // Store cleanup function for event handler +} + +type EventData struct { + Message string `json:"message"` + Timestamp string `json:"timestamp"` +} + +// NewGinService creates a new GinService instance +func NewGinService() *GinService { + // Create a new Gin router + ginEngine := gin.New() + + // Add middlewares + ginEngine.Use(gin.Recovery()) + ginEngine.Use(LoggingMiddleware()) + + service := &GinService{ + ginEngine: ginEngine, + users: []User{ + {ID: 1, Name: "Alice", Email: "alice@example.com", CreatedAt: time.Now().Add(-72 * time.Hour)}, + {ID: 2, Name: "Bob", Email: "bob@example.com", CreatedAt: time.Now().Add(-48 * time.Hour)}, + {ID: 3, Name: "Charlie", Email: "charlie@example.com", CreatedAt: time.Now().Add(-24 * time.Hour)}, + }, + nextID: 4, + maxUsers: 1000, // Limit to prevent unbounded slice growth + } + + // Define routes + service.setupRoutes() + + return service +} + +// ServiceName returns the name of the service +func (s *GinService) ServiceName() string { + return "Gin API Service" +} + +// ServiceStartup is called when the service starts +func (s *GinService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + // You can access the application instance via ctx + s.app = application.Get() + + // Register an event handler that can be triggered from the frontend + // Store the cleanup function for proper resource management + s.removeEventHandler = s.app.Event.On("gin-api-event", func(event *application.CustomEvent) { + // Log the event data + // Parse the event data + s.app.Logger.Info("Received event from frontend", "data", event.Data) + + // You could also emit an event back to the frontend + s.app.Event.Emit("gin-api-response", map[string]interface{}{ + "message": "Response from Gin API Service", + "time": time.Now().Format(time.RFC3339), + }) + }) + + return nil +} + +// ServiceShutdown is called when the service shuts down +func (s *GinService) ServiceShutdown(ctx context.Context) error { + // Clean up event handler to prevent memory leaks + if s.removeEventHandler != nil { + s.removeEventHandler() + } + return nil +} + +// ServeHTTP implements the http.Handler interface +func (s *GinService) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // All other requests go to the Gin router + s.ginEngine.ServeHTTP(w, r) +} + +// setupRoutes configures the API routes +func (s *GinService) setupRoutes() { + // Basic info endpoint + s.ginEngine.GET("/info", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "service": "Gin API Service", + "version": "1.0.0", + "time": time.Now().Format(time.RFC3339), + }) + }) + + // Users group + users := s.ginEngine.Group("/users") + { + // Get all users + users.GET("", func(c *gin.Context) { + s.mu.RLock() + defer s.mu.RUnlock() + c.JSON(http.StatusOK, s.users) + }) + + // Get user by ID + users.GET("/:id", func(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) + return + } + + s.mu.RLock() + defer s.mu.RUnlock() + + for _, user := range s.users { + if user.ID == id { + c.JSON(http.StatusOK, user) + return + } + } + + c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) + }) + + // Create a new user + users.POST("", func(c *gin.Context) { + var newUser User + if err := c.ShouldBindJSON(&newUser); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Validate required fields + if newUser.Name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Name is required"}) + return + } + if newUser.Email == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Email is required"}) + return + } + // Basic email validation (consider using a proper validator library in production) + if !strings.Contains(newUser.Email, "@") { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email format"}) + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + // Check if we've reached the maximum number of users + if len(s.users) >= s.maxUsers { + c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Maximum number of users reached"}) + return + } + + // Set the ID and creation time + newUser.ID = s.nextID + newUser.CreatedAt = time.Now() + s.nextID++ + + // Add to the users slice + s.users = append(s.users, newUser) + + c.JSON(http.StatusCreated, newUser) + + // Emit an event to notify about the new user + s.app.Event.Emit("user-created", newUser) + }) + + // Delete a user + users.DELETE("/:id", func(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + for i, user := range s.users { + if user.ID == id { + // Remove the user from the slice + s.users = append(s.users[:i], s.users[i+1:]...) + c.JSON(http.StatusOK, gin.H{"message": "User deleted"}) + return + } + } + + c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) + }) + } +} + +// LoggingMiddleware is a Gin middleware that logs request details +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Start timer + start := time.Now() + + // Process request + c.Next() + + // Calculate latency + latency := time.Since(start) + + // Log request details + statusCode := c.Writer.Status() + clientIP := c.ClientIP() + method := c.Request.Method + path := c.Request.URL.Path + + // Get the application instance + app := application.Get() + if app != nil { + app.Logger.Info("HTTP Request", + "status", statusCode, + "method", method, + "path", path, + "ip", clientIP, + "latency", latency, + ) + } + } +} diff --git a/v3/examples/hide-window/main.go b/v3/examples/hide-window/main.go new file mode 100644 index 000000000..1a65c7498 --- /dev/null +++ b/v3/examples/hide-window/main.go @@ -0,0 +1,69 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/icons" + "log" + "runtime" +) + +func main() { + app := application.New(application.Options{ + Name: "Hide Window Demo", + Description: "A test of Hidden window and display it", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: false, + }, + }) + + systemTray := app.SystemTray.New() + + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 500, + Height: 800, + Frameless: false, + AlwaysOnTop: false, + Hidden: false, + DisableResize: false, + Windows: application.WindowsWindow{ + HiddenOnTaskbar: true, + }, + }) + + window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + window.Hide() + e.Cancel() + }) + + if runtime.GOOS == "darwin" { + systemTray.SetTemplateIcon(icons.SystrayMacTemplate) + } + + // Click Dock icon tigger application show + app.Event.OnApplicationEvent(events.Mac.ApplicationShouldHandleReopen, func(event *application.ApplicationEvent) { + println("reopen") + window.Show() + }) + + myMenu := app.NewMenu() + myMenu.Add("Show").OnClick(func(ctx *application.Context) { + window.Show() + }) + + myMenu.Add("Quit").OnClick(func(ctx *application.Context) { + app.Quit() + }) + + systemTray.SetMenu(myMenu) + systemTray.OnClick(func() { + window.Show() + }) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/html-dnd-api/README.md b/v3/examples/html-dnd-api/README.md new file mode 100644 index 000000000..1b06f3882 --- /dev/null +++ b/v3/examples/html-dnd-api/README.md @@ -0,0 +1,43 @@ +# HTML Drag and Drop API Example + +This example should demonstrate whether the [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API") works correctly. + +## Expected Behaviour + +When dragging the "draggable", in the console should be printed: +1. "dragstart" once +2. "drag" many times +3. "dragend" once + +When dragging the "draggable" on the drop target, the inner text of the latter shoud change and in the console should be printed: +1. "dragstart" once +2. "drag" many times +3. "dragenter" once +4. "dragover" many times (alternating with "drag") +5. - "drop" once (in case of a drop inside the drop target) + - "dragleave" once (in case the draggable div leaves the drop target) +6. "dragend" once + +## Running the example + +To run the example, simply run the following command: + +```bash +go run main.go +``` + +## Building the example + +To build the example in debug mode, simply run the following command: + +```bash +wails3 task build +``` + +# Status + +| Platform | Status | +|----------|-------------| +| Mac | Working | +| Windows | Not Working | +| Linux | | diff --git a/v3/examples/html-dnd-api/assets/index.html b/v3/examples/html-dnd-api/assets/index.html new file mode 100644 index 000000000..a64b9378a --- /dev/null +++ b/v3/examples/html-dnd-api/assets/index.html @@ -0,0 +1,75 @@ + + + + + Title + + + +

HTML Drag and Drop API Demo

+
+ +
draggable
+ +
drop target
+ + + + + + diff --git a/v3/examples/html-dnd-api/main.go b/v3/examples/html-dnd-api/main.go new file mode 100644 index 000000000..549523ef1 --- /dev/null +++ b/v3/examples/html-dnd-api/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "HTML Drag and Drop API Demo", + Description: "A demo of the HTML Drag and drop API", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Drag-n-drop Demo", + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/ignore-mouse/README.md b/v3/examples/ignore-mouse/README.md new file mode 100644 index 000000000..596d2c015 --- /dev/null +++ b/v3/examples/ignore-mouse/README.md @@ -0,0 +1,19 @@ +# Window Example + +This example is a demonstration of how to disable or enable mouse events. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | | +| Windows | Working | +| Linux | | diff --git a/v3/examples/ignore-mouse/main.go b/v3/examples/ignore-mouse/main.go new file mode 100644 index 000000000..1fc0304f2 --- /dev/null +++ b/v3/examples/ignore-mouse/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "WebviewWindow Demo", + Description: "A demo of the WebviewWindow API", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: false, + }, + }) + + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 800, + Height: 600, + Title: "Ignore Mouse Example", + URL: "https://wails.io", + IgnoreMouseEvents: false, + }) + + window.SetIgnoreMouseEvents(true) + log.Println("IgnoreMouseEvents set", window.IsIgnoreMouseEvents()) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/keybindings/README.md b/v3/examples/keybindings/README.md new file mode 100644 index 000000000..2180cc9d0 --- /dev/null +++ b/v3/examples/keybindings/README.md @@ -0,0 +1,21 @@ +# Keybindings Example + +This simple example demonstrates how to use keybindings in your application. +Run the example and press `Ctrl/CMD+Shift+C` to center the focused window. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | + diff --git a/v3/examples/keybindings/main.go b/v3/examples/keybindings/main.go new file mode 100644 index 000000000..b07bf366b --- /dev/null +++ b/v3/examples/keybindings/main.go @@ -0,0 +1,50 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "log" +) + +func main() { + app := application.New(application.Options{ + Name: "Key Bindings Demo", + Description: "A demo of the Key Bindings Options", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + KeyBindings: map[string]func(window application.Window){ + "shift+ctrl+c": func(window application.Window) { + window.Center() + }, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Name: "Window 1", + Title: "Window 1", + URL: "https://wails.io", + KeyBindings: map[string]func(window application.Window){ + "F12": func(window application.Window) { + window.OpenDevTools() + }, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Name: "Window 2", + Title: "Window 2", + URL: "https://google.com", + KeyBindings: map[string]func(window application.Window){ + "F12": func(window application.Window) { + println("Window 2: Toggle Dev Tools") + }, + }, + }) + + err := app.Run() + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/linux_status.org b/v3/examples/linux_status.org new file mode 100644 index 000000000..fdb31ac27 --- /dev/null +++ b/v3/examples/linux_status.org @@ -0,0 +1,28 @@ + +* Status + +| Example | Status | Notes | +|--------------+-----------------+-------------------------------------------------------------------------------| +| binding | works | | +| build | works | removed OS X specific env variables from default target | +| clipboard | works | | +| contextmenus | works (partial) | | +| dev | works | purpose? | +| dialogs | | broken | +| drag-n-drop | works | | +| events | partial | receives WailsEvents - not ApplicationEvents | +| frameless | partial | drag areas do not function | +| hide-window | partial | crash on windowShow - believe this is because window is being destroyed not hidden | +| keybindings | working | | +| menu | working | Lock WebviewWindow Resize isn't correct | +| oauth | failed | Can't type in window - but can paste - redirect failed as well | +| plain | works | | +| plugins | works | Might should provide example commands or something. | +| screen | failed | | +| server | works | | +| systray | works | | +| video | works | binary is named 'frameless' | +| window | partial | Screens related stuff isn't implemented | +| wml | partial | | + + diff --git a/v3/examples/liquid-glass/.gitignore b/v3/examples/liquid-glass/.gitignore new file mode 100644 index 000000000..45d2406b0 --- /dev/null +++ b/v3/examples/liquid-glass/.gitignore @@ -0,0 +1 @@ +liquid-glass-demo diff --git a/v3/examples/liquid-glass/README.md b/v3/examples/liquid-glass/README.md new file mode 100644 index 000000000..33875af54 --- /dev/null +++ b/v3/examples/liquid-glass/README.md @@ -0,0 +1,70 @@ +# Liquid Glass Demo for Wails v3 + +This demo showcases the native Liquid Glass effect available in macOS 15.0+ with fallback to NSVisualEffectView for older systems. + +## Features Demonstrated + +### Window Styles + +1. **Light Glass** - Clean, light appearance with no tint +2. **Dark Glass** - Dark themed glass effect +3. **Vibrant Glass** - Enhanced vibrant effect for maximum transparency +4. **Tinted Glass** - Blue tinted glass with custom RGBA color +5. **Sheet Material** - Using specific NSVisualEffectMaterialSheet +6. **HUD Window** - Ultra-light HUD window material +7. **Content Background** - Content background material with warm tint + +### Customization Options + +- **Style**: `LiquidGlassStyleAutomatic`, `LiquidGlassStyleLight`, `LiquidGlassStyleDark`, `LiquidGlassStyleVibrant` +- **Material**: Direct NSVisualEffectMaterial selection (when NSGlassEffectView is not available) + - `NSVisualEffectMaterialAppearanceBased` + - `NSVisualEffectMaterialLight` + - `NSVisualEffectMaterialDark` + - `NSVisualEffectMaterialSheet` + - `NSVisualEffectMaterialHUDWindow` + - `NSVisualEffectMaterialContentBackground` + - `NSVisualEffectMaterialUnderWindowBackground` + - `NSVisualEffectMaterialUnderPageBackground` + - And more... +- **CornerRadius**: Rounded corners (0 for square corners) +- **TintColor**: Custom RGBA tint overlay +- **GroupID**: For grouping multiple glass windows (future feature) +- **GroupSpacing**: Spacing between grouped windows (future feature) + +### Running the Demo + +```bash +go build -o liquid-glass-demo . +./liquid-glass-demo +``` + +### Requirements + +- macOS 10.14+ (best experience on macOS 26.0+ with native NSGlassEffectView) +- Wails v3 + +### Implementation Details + +The implementation uses: +- Native `NSGlassEffectView` on macOS 26.0+ for authentic glass effect +- Falls back to `NSVisualEffectView` on older systems +- Runtime detection using `NSClassFromString` for compatibility +- Key-Value Coding (KVC) for dynamic property setting + +### Example Usage + +```go +window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, // Make window draggable + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleLight, + Material: application.NSVisualEffectMaterialHUDWindow, + CornerRadius: 20.0, + TintColor: &application.RGBA{Red: 0, Green: 100, Blue: 200, Alpha: 50}, + }, + }, +}) +``` \ No newline at end of file diff --git a/v3/examples/liquid-glass/index.html b/v3/examples/liquid-glass/index.html new file mode 100644 index 000000000..7f9ff4545 --- /dev/null +++ b/v3/examples/liquid-glass/index.html @@ -0,0 +1,50 @@ + + + + + + Wails Liquid Glass + + + +
+ +

LIQUID GLASS

+
+ + \ No newline at end of file diff --git a/v3/examples/liquid-glass/main.go b/v3/examples/liquid-glass/main.go new file mode 100644 index 000000000..9562c5930 --- /dev/null +++ b/v3/examples/liquid-glass/main.go @@ -0,0 +1,235 @@ +package main + +import ( + _ "embed" + "encoding/base64" + "log" + "runtime" + "strings" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed index.html +var indexHTML string + +//go:embed wails-logo.png +var wailsLogo []byte + +func main() { + app := application.New(application.Options{ + Name: "Wails Liquid Glass Demo", + Description: "Demonstrates the native Liquid Glass effect on macOS", + }) + + // Check if running on macOS + if runtime.GOOS != "darwin" { + // Show dialog for non-macOS platforms + application.InfoDialog(). + SetTitle("macOS Only Demo"). + SetMessage("The Liquid Glass effect is a macOS-specific feature that uses native NSGlassEffectView (macOS 15.0+) or NSVisualEffectView.\n\nThis demo is not available on " + runtime.GOOS + "."). + Show() + return + } + + // Convert logo to base64 data URI + logoDataURI := "data:image/png;base64," + base64.StdEncoding.EncodeToString(wailsLogo) + + // Create different HTML for each window + lightHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + lightHTML = strings.Replace(lightHTML, "LIQUID GLASS", "Light Style", 1) + + darkHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + darkHTML = strings.Replace(darkHTML, "LIQUID GLASS", "Dark Style", 1) + + vibrantHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + vibrantHTML = strings.Replace(vibrantHTML, "LIQUID GLASS", "Vibrant Style", 1) + + tintedHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + tintedHTML = strings.Replace(tintedHTML, "LIQUID GLASS", "Blue Tint", 1) + + sheetHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + sheetHTML = strings.Replace(sheetHTML, "LIQUID GLASS", "Sheet Material", 1) + + hudHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + hudHTML = strings.Replace(hudHTML, "LIQUID GLASS", "HUD Window", 1) + + contentHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1) + contentHTML = strings.Replace(contentHTML, "LIQUID GLASS", "Content Background", 1) + + // Window 1: Light style with no tint + window1 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Light Glass", + Width: 350, + Height: 280, + X: 100, + Y: 100, + Frameless: true, + EnableDragAndDrop: false, + HTML: lightHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleLight, + Material: application.NSVisualEffectMaterialAuto, + CornerRadius: 20.0, + TintColor: nil, + }, + }, + }) + + // Window 2: Dark style + window2 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Dark Glass", + Width: 350, + Height: 280, + X: 500, + Y: 100, + Frameless: true, + EnableDragAndDrop: false, + HTML: darkHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleDark, + Material: application.NSVisualEffectMaterialAuto, + CornerRadius: 20.0, + TintColor: nil, + }, + }, + }) + + // Window 3: Vibrant style + window3 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Vibrant Glass", + Width: 350, + Height: 280, + X: 900, + Y: 100, + Frameless: true, + EnableDragAndDrop: false, + HTML: vibrantHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleVibrant, + Material: application.NSVisualEffectMaterialAuto, + CornerRadius: 20.0, + TintColor: nil, + }, + }, + }) + + // Window 4: Blue tinted glass + window4 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Tinted Glass", + Width: 350, + Height: 280, + X: 300, + Y: 420, + Frameless: true, + EnableDragAndDrop: false, + HTML: tintedHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleLight, + Material: application.NSVisualEffectMaterialAuto, + CornerRadius: 25.0, // Different corner radius + TintColor: &application.RGBA{Red: 0, Green: 100, Blue: 200, Alpha: 50}, // Blue tint + }, + }, + }) + + // Window 5: Using specific NSVisualEffectMaterial + window5 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Sheet Material", + Width: 350, + Height: 280, + X: 700, + Y: 420, + Frameless: true, + EnableDragAndDrop: false, + HTML: sheetHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleAutomatic, // Automatic style + Material: application.NSVisualEffectMaterialSheet, // Specific material + CornerRadius: 15.0, // Different corner radius + TintColor: nil, + }, + }, + }) + + // Window 6: HUD Window Material (very light, translucent) + window6 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "HUD Window", + Width: 350, + Height: 280, + X: 100, + Y: 740, + Frameless: true, + EnableDragAndDrop: false, + HTML: hudHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleAutomatic, + Material: application.NSVisualEffectMaterialHUDWindow, // HUD Window material - very light + CornerRadius: 30.0, // Larger corner radius + TintColor: nil, + }, + }, + }) + + // Window 7: Content Background Material + window7 := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Content Background", + Width: 350, + Height: 280, + X: 500, + Y: 740, + Frameless: true, + EnableDragAndDrop: false, + HTML: contentHTML, + InitialPosition: application.WindowXY, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropLiquidGlass, + InvisibleTitleBarHeight: 500, + LiquidGlass: application.MacLiquidGlass{ + Style: application.LiquidGlassStyleAutomatic, + Material: application.NSVisualEffectMaterialContentBackground, // Content background + CornerRadius: 10.0, // Smaller corner radius + TintColor: &application.RGBA{Red: 0, Green: 200, Blue: 100, Alpha: 30}, // Warm tint + }, + }, + }) + + // Show all windows + window1.Show() + window2.Show() + window3.Show() + window4.Show() + window5.Show() + window6.Show() + window7.Show() + + // Run the application + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/liquid-glass/wails-logo.png b/v3/examples/liquid-glass/wails-logo.png new file mode 100644 index 000000000..e65c582ff Binary files /dev/null and b/v3/examples/liquid-glass/wails-logo.png differ diff --git a/v3/examples/menu/README.md b/v3/examples/menu/README.md new file mode 100644 index 000000000..cc926df73 --- /dev/null +++ b/v3/examples/menu/README.md @@ -0,0 +1,27 @@ +# Menu Example + +This example is a demonstration of different ways to create applications without using npm. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | + +# Known Issues + +- [Resize cursor still visible when not resizable](https://github.com/orgs/wailsapp/projects/6/views/1?pane=issue&itemId=40962163) + +--- + +Icon attribution: [Click icons created by kusumapotter - Flaticon](https://www.flaticon.com/free-icons/click) \ No newline at end of file diff --git a/v3/examples/menu/icon.png b/v3/examples/menu/icon.png new file mode 100644 index 000000000..e934687ca Binary files /dev/null and b/v3/examples/menu/icon.png differ diff --git a/v3/examples/menu/main.go b/v3/examples/menu/main.go new file mode 100644 index 000000000..0349eceb6 --- /dev/null +++ b/v3/examples/menu/main.go @@ -0,0 +1,157 @@ +package main + +import ( + _ "embed" + "log" + "runtime" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed icon.png +var clickBitmap []byte + +func main() { + + app := application.New(application.Options{ + Name: "Menu Demo", + Description: "A demo of the menu system", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a custom menu + menu := app.NewMenu() + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + menu.AddRole(application.FileMenu) + menu.AddRole(application.EditMenu) + menu.AddRole(application.WindowMenu) + menu.AddRole(application.HelpMenu) + + // Let's make a "Demo" menu + myMenu := menu.AddSubmenu("Demo") + + // Hidden menu item that can be unhidden + hidden := myMenu.Add("I was hidden").SetHidden(true) + myMenu.Add("Toggle the hidden menu").OnClick(func(ctx *application.Context) { + hidden.SetHidden(!hidden.Hidden()) + }) + + // Disabled menu item + myMenu.Add("Not Enabled").SetEnabled(false) + + // Click callbacks + myMenu.Add("Click Me!").SetAccelerator("CmdOrCtrl+l").OnClick(func(ctx *application.Context) { + switch ctx.ClickedMenuItem().Label() { + case "Click Me!": + ctx.ClickedMenuItem().SetLabel("Thanks mate!") + case "Thanks mate!": + ctx.ClickedMenuItem().SetLabel("Click Me!") + } + }) + + // You can control the current window from the menu + myMenu.Add("Lock WebviewWindow Resize").OnClick(func(ctx *application.Context) { + if app.Window.Current().Resizable() { + app.Window.Current().SetResizable(false) + ctx.ClickedMenuItem().SetLabel("Unlock WebviewWindow Resize") + } else { + app.Window.Current().SetResizable(true) + ctx.ClickedMenuItem().SetLabel("Lock WebviewWindow Resize") + } + }) + + myMenu.AddSeparator() + + // Checkboxes will tell you their new state so you don't need to track it + myMenu.AddCheckbox("My checkbox", true).OnClick(func(context *application.Context) { + println("Clicked checkbox. Checked:", context.ClickedMenuItem().Checked()) + }) + myMenu.AddSeparator() + + // Callbacks can be shared. This is useful for radio groups + radioCallback := func(ctx *application.Context) { + menuItem := ctx.ClickedMenuItem() + menuItem.SetLabel(menuItem.Label() + "!") + } + + // Radio groups are created implicitly by placing radio items next to each other in a menu + myMenu.AddRadio("Radio 1", true).OnClick(radioCallback) + myMenu.AddRadio("Radio 2", false).OnClick(radioCallback) + myMenu.AddRadio("Radio 3", false).OnClick(radioCallback) + + // Submenus are also supported + submenu := myMenu.AddSubmenu("Submenu") + submenu.Add("Submenu item 1") + submenu.Add("Submenu item 2") + submenu.Add("Submenu item 3") + + myMenu.AddSeparator() + + beatles := myMenu.Add("Hello").OnClick(func(*application.Context) { + println("The beatles would be proud") + }) + myMenu.Add("Toggle the menuitem above").OnClick(func(*application.Context) { + if beatles.Enabled() { + beatles.SetEnabled(false) + beatles.SetLabel("Goodbye") + } else { + beatles.SetEnabled(true) + beatles.SetLabel("Hello") + } + }) + myMenu.Add("Hide the beatles").OnClick(func(ctx *application.Context) { + if beatles.Hidden() { + ctx.ClickedMenuItem().SetLabel("Hide the beatles!") + beatles.SetHidden(false) + } else { + beatles.SetHidden(true) + ctx.ClickedMenuItem().SetLabel("Unhide the beatles!") + } + }) + + myMenu.AddSeparator() + + coffee := myMenu.Add("Request Coffee").OnClick(func(*application.Context) { + println("Coffee dispatched. Productivity +10!") + }) + + myMenu.Add("Toggle coffee availability").OnClick(func(*application.Context) { + if coffee.Enabled() { + coffee.SetEnabled(false) + coffee.SetLabel("Coffee Machine Broken") + println("Alert: Developer morale critically low.") + } else { + coffee.SetEnabled(true) + coffee.SetLabel("Request Coffee") + println("All systems nominal. Coffee restored.") + } + }) + + myMenu.Add("Hide the coffee option").OnClick(func(ctx *application.Context) { + if coffee.Hidden() { + ctx.ClickedMenuItem().SetLabel("Hide the coffee option") + coffee.SetHidden(false) + println("Coffee menu item has been resurrected!") + } else { + coffee.SetHidden(true) + ctx.ClickedMenuItem().SetLabel("Unhide the coffee option") + println("The coffee option has vanished into the void.") + } + }) + + app.Menu.Set(menu) + + window := app.Window.New().SetBackgroundColour(application.NewRGB(33, 37, 41)) + window.SetMenu(menu) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/menu/menu_demo b/v3/examples/menu/menu_demo new file mode 100755 index 000000000..78926ad95 Binary files /dev/null and b/v3/examples/menu/menu_demo differ diff --git a/v3/examples/notifications/README.md b/v3/examples/notifications/README.md new file mode 100644 index 000000000..ad12c3f40 --- /dev/null +++ b/v3/examples/notifications/README.md @@ -0,0 +1,59 @@ +# Welcome to Your New Wails3 Project! + +Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running. + +## Getting Started + +1. Navigate to your project directory in the terminal. + +2. To run your application in development mode, use the following command: + + ``` + wails3 dev + ``` + + This will start your application and enable hot-reloading for both frontend and backend changes. + +3. To build your application for production, use: + + ``` + wails3 build + ``` + + This will create a production-ready executable in the `build` directory. + +## Exploring Wails3 Features + +Now that you have your project set up, it's time to explore the features that Wails3 offers: + +1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications. + +2. **Run an example**: To run any of the examples, navigate to the example's directory and use: + + ``` + go run . + ``` + + Note: Some examples may be under development during the alpha phase. + +3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3.wails.io/) for in-depth guides and API references. + +4. **Join the community**: Have questions or want to share your progress? Join the [Wails Discord](https://discord.gg/JDdSxwjhGf) or visit the [Wails discussions on GitHub](https://github.com/wailsapp/wails/discussions). + +## Project Structure + +Take a moment to familiarize yourself with your project structure: + +- `frontend/`: Contains your frontend code (HTML, CSS, JavaScript/TypeScript) +- `main.go`: The entry point of your Go backend +- `app.go`: Define your application structure and methods here +- `wails.json`: Configuration file for your Wails project + +## Next Steps + +1. Modify the frontend in the `frontend/` directory to create your desired UI. +2. Add backend functionality in `main.go`. +3. Use `wails3 dev` to see your changes in real-time. +4. When ready, build your application with `wails3 build`. + +Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community. diff --git a/v3/examples/notifications/Taskfile.yml b/v3/examples/notifications/Taskfile.yml new file mode 100644 index 000000000..1455cd70c --- /dev/null +++ b/v3/examples/notifications/Taskfile.yml @@ -0,0 +1,34 @@ +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "Notifications\\ Demo" + BIN_DIR: "bin" + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + diff --git a/v3/examples/notifications/build/Taskfile.yml b/v3/examples/notifications/build/Taskfile.yml new file mode 100644 index 000000000..5f3517efc --- /dev/null +++ b/v3/examples/notifications/build/Taskfile.yml @@ -0,0 +1,86 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + 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 + + build:frontend: + label: build:frontend (PRODUCTION={{.PRODUCTION}}) + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/**/* + deps: + - task: install:frontend:deps + - task: generate:bindings + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + cmds: + - npm run {{.BUILD_COMMAND}} -q + env: + PRODUCTION: '{{.PRODUCTION | default "false"}}' + vars: + BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build:dev{{end}}' + + + generate:bindings: + label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}}) + summary: Generates bindings for the frontend + 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 -ts + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + sources: + - "appicon.png" + generates: + - "darwin/icons.icns" + - "windows/icon.ico" + cmds: + - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico + + dev:frontend: + summary: Runs the frontend in development mode + dir: frontend + deps: + - task: install:frontend:deps + cmds: + - npm 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 . diff --git a/v3/examples/notifications/build/appicon.png b/v3/examples/notifications/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/notifications/build/appicon.png differ diff --git a/v3/examples/notifications/build/config.yml b/v3/examples/notifications/build/config.yml new file mode 100644 index 000000000..bc09a6d28 --- /dev/null +++ b/v3/examples/notifications/build/config.yml @@ -0,0 +1,62 @@ +# 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: "My Company" # The name of the company + productName: "My Product" # The name of the application + productIdentifier: "com.mycompany.myproduct" # The unique product identifier + description: "A program that does X" # The application description + copyright: "(c) 2025, My Company" # Copyright text + comments: "Some Product Comments" # Comments + version: "v0.0.1" # The application version + +# Dev mode configuration +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - 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 + +# Other data +other: + - name: My Other Data \ No newline at end of file diff --git a/v3/examples/notifications/build/darwin/Info.dev.plist b/v3/examples/notifications/build/darwin/Info.dev.plist new file mode 100644 index 000000000..3a5b9735f --- /dev/null +++ b/v3/examples/notifications/build/darwin/Info.dev.plist @@ -0,0 +1,32 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + Notifications Demo + CFBundleIdentifier + com.wails.notifications-demo + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/v3/examples/notifications/build/darwin/Info.plist b/v3/examples/notifications/build/darwin/Info.plist new file mode 100644 index 000000000..464270019 --- /dev/null +++ b/v3/examples/notifications/build/darwin/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + Notifications Demo + CFBundleIdentifier + com.wails.notifications-demo + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + + \ No newline at end of file diff --git a/v3/examples/notifications/build/darwin/Taskfile.yml b/v3/examples/notifications/build/darwin/Taskfile.yml new file mode 100644 index 000000000..3b6a9dc99 --- /dev/null +++ b/v3/examples/notifications/build/darwin/Taskfile.yml @@ -0,0 +1,80 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Creates a production build of the application + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.OUTPUT}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{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=10.15" + CGO_LDFLAGS: "-mmacosx-version-min=10.15" + MACOSX_DEPLOYMENT_TARGET: "10.15" + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + 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: + - 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" + + package: + summary: Packages a production build of the application into a `.app` bundle + deps: + - task: build + vars: + PRODUCTION: "true" + 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,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS + - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents + + run: + cmds: + - mkdir -p {{.BIN_DIR}}/dev/{{.APP_NAME}}.app/Contents/{MacOS,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/dev/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/dev/{{.APP_NAME}}.app/Contents/MacOS + - cp build/darwin/Info.dev.plist {{.BIN_DIR}}/dev/{{.APP_NAME}}.app/Contents/Info.plist + - codesign --force --deep --sign - {{.BIN_DIR}}/dev/{{.APP_NAME}}.app + - '{{.BIN_DIR}}/dev/{{.APP_NAME}}.app/Contents/MacOS/{{.APP_NAME}}' diff --git a/v3/examples/notifications/build/darwin/icons.icns b/v3/examples/notifications/build/darwin/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/notifications/build/darwin/icons.icns differ diff --git a/v3/examples/notifications/build/linux/Taskfile.yml b/v3/examples/notifications/build/linux/Taskfile.yml new file mode 100644 index 000000000..560cc9c92 --- /dev/null +++ b/v3/examples/notifications/build/linux/Taskfile.yml @@ -0,0 +1,119 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Linux + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application for Linux + deps: + - task: build + vars: + PRODUCTION: "true" + 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 + vars: + PRODUCTION: "true" + - task: generate:dotdesktop + cmds: + - cp {{.APP_BINARY}} {{.APP_NAME}} + - cp ../../appicon.png appicon.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: '../../appicon.png' + DESKTOP_FILE: '../{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../../bin' + + create:deb: + summary: Creates a deb package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:deb + + create:rpm: + summary: Creates a rpm package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:rpm + + create:aur: + summary: Creates a arch linux packager package + deps: + - task: build + vars: + PRODUCTION: "true" + 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: 'appicon' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/notifications/build/linux/appimage/build.sh b/v3/examples/notifications/build/linux/appimage/build.sh new file mode 100644 index 000000000..85901c34e --- /dev/null +++ b/v3/examples/notifications/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/v3/examples/notifications/build/linux/nfpm/nfpm.yaml b/v3/examples/notifications/build/linux/nfpm/nfpm.yaml new file mode 100644 index 000000000..c2cb7cd81 --- /dev/null +++ b/v3/examples/notifications/build/linux/nfpm/nfpm.yaml @@ -0,0 +1,50 @@ +# 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: "notifications" +arch: ${GOARCH} +platform: "linux" +version: "0.1.0" +section: "default" +priority: "extra" +maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> +description: "My Product Description" +vendor: "My Company" +homepage: "https://wails.io" +license: "MIT" +release: "1" + +contents: + - src: "./bin/notifications" + dst: "/usr/local/bin/notifications" + - src: "./build/appicon.png" + dst: "/usr/share/icons/hicolor/128x128/apps/notifications.png" + - src: "./build/linux/notifications.desktop" + dst: "/usr/share/applications/notifications.desktop" + +depends: + - gtk3 + - libwebkit2gtk + +# replaces: +# - foobar +# provides: +# - bar +# depends: +# - gtk3 +# - libwebkit2gtk +# recommends: +# - whatever +# suggests: +# - something-else +# conflicts: +# - not-foo +# - not-bar +# changelog: "changelog.yaml" +# scripts: +# preinstall: ./build/linux/nfpm/scripts/preinstall.sh +# postinstall: ./build/linux/nfpm/scripts/postinstall.sh +# preremove: ./build/linux/nfpm/scripts/preremove.sh +# postremove: ./build/linux/nfpm/scripts/postremove.sh diff --git a/v3/examples/notifications/build/linux/nfpm/scripts/postinstall.sh b/v3/examples/notifications/build/linux/nfpm/scripts/postinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/notifications/build/linux/nfpm/scripts/postinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/notifications/build/linux/nfpm/scripts/postremove.sh b/v3/examples/notifications/build/linux/nfpm/scripts/postremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/notifications/build/linux/nfpm/scripts/postremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/notifications/build/linux/nfpm/scripts/preinstall.sh b/v3/examples/notifications/build/linux/nfpm/scripts/preinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/notifications/build/linux/nfpm/scripts/preinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/notifications/build/linux/nfpm/scripts/preremove.sh b/v3/examples/notifications/build/linux/nfpm/scripts/preremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/notifications/build/linux/nfpm/scripts/preremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/notifications/build/windows/Taskfile.yml b/v3/examples/notifications/build/windows/Taskfile.yml new file mode 100644 index 000000000..be6e4125e --- /dev/null +++ b/v3/examples/notifications/build/windows/Taskfile.yml @@ -0,0 +1,63 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Windows + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - task: generate:syso + - go build {{.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 .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: windows + CGO_ENABLED: 0 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application into a `.exe` bundle + cmds: + - task: create:nsis:installer + + 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 + vars: + PRODUCTION: "true" + cmds: + # Create the Microsoft WebView2 bootstrapper if it doesn't exist + - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis" + - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + + run: + cmds: + - '{{.BIN_DIR}}\\{{.APP_NAME}}.exe' diff --git a/v3/examples/notifications/build/windows/icon.ico b/v3/examples/notifications/build/windows/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/notifications/build/windows/icon.ico differ diff --git a/v3/examples/notifications/build/windows/info.json b/v3/examples/notifications/build/windows/info.json new file mode 100644 index 000000000..850b2b5b0 --- /dev/null +++ b/v3/examples/notifications/build/windows/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.1.0" + }, + "info": { + "0000": { + "ProductVersion": "0.1.0", + "CompanyName": "My Company", + "FileDescription": "My Product Description", + "LegalCopyright": "© now, My Company", + "ProductName": "My Product", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/notifications/build/windows/nsis/project.nsi b/v3/examples/notifications/build/windows/nsis/project.nsi new file mode 100644 index 000000000..4cb18e04f --- /dev/null +++ b/v3/examples/notifications/build/windows/nsis/project.nsi @@ -0,0 +1,112 @@ +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 "notifications" +## !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 "© now, 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 +#### +## 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. +InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). +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.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.deleteUninstaller +SectionEnd diff --git a/v3/examples/notifications/build/windows/nsis/wails_tools.nsh b/v3/examples/notifications/build/windows/nsis/wails_tools.nsh new file mode 100644 index 000000000..c47c784a4 --- /dev/null +++ b/v3/examples/notifications/build/windows/nsis/wails_tools.nsh @@ -0,0 +1,212 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "notifications" +!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 "© now, 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 REQUEST_EXECUTION_LEVEL + !define REQUEST_EXECUTION_LEVEL "admin" +!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 + 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" +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + DeleteRegKey HKLM "${UNINST_KEY}" +!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 \ No newline at end of file diff --git a/v3/examples/notifications/build/windows/wails.exe.manifest b/v3/examples/notifications/build/windows/wails.exe.manifest new file mode 100644 index 000000000..0299e62ca --- /dev/null +++ b/v3/examples/notifications/build/windows/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v3/examples/notifications/frontend/Inter Font License.txt b/v3/examples/notifications/frontend/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/examples/notifications/frontend/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.ts b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.ts new file mode 100644 index 000000000..71eda3bb9 --- /dev/null +++ b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as NotificationService from "./notificationservice.js"; +export { + NotificationService +}; + +export { + NotificationAction, + NotificationCategory, + NotificationOptions +} from "./models.js"; diff --git a/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.ts b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.ts new file mode 100644 index 000000000..d7f48edfe --- /dev/null +++ b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/models.ts @@ -0,0 +1,107 @@ +// 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"; + +/** + * NotificationAction represents an action button for a notification. + */ +export class NotificationAction { + "id"?: string; + "title"?: string; + + /** + * (macOS-specific) + */ + "destructive"?: boolean; + + /** Creates a new NotificationAction instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new NotificationAction instance from a string or object. + */ + static createFrom($$source: any = {}): NotificationAction { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new NotificationAction($$parsedSource as Partial); + } +} + +/** + * NotificationCategory groups actions for notifications. + */ +export class NotificationCategory { + "id"?: string; + "actions"?: NotificationAction[]; + "hasReplyField"?: boolean; + "replyPlaceholder"?: string; + "replyButtonTitle"?: string; + + /** Creates a new NotificationCategory instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new NotificationCategory instance from a string or object. + */ + static createFrom($$source: any = {}): NotificationCategory { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("actions" in $$parsedSource) { + $$parsedSource["actions"] = $$createField1_0($$parsedSource["actions"]); + } + return new NotificationCategory($$parsedSource as Partial); + } +} + +/** + * NotificationOptions contains configuration for a notification + */ +export class NotificationOptions { + "id": string; + "title": string; + + /** + * (macOS and Linux only) + */ + "subtitle"?: string; + "body"?: string; + "categoryId"?: string; + "data"?: { [_: string]: any }; + + /** Creates a new NotificationOptions instance. */ + constructor($$source: Partial = {}) { + if (!("id" in $$source)) { + this["id"] = ""; + } + if (!("title" in $$source)) { + this["title"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new NotificationOptions instance from a string or object. + */ + static createFrom($$source: any = {}): NotificationOptions { + const $$createField5_0 = $$createType2; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("data" in $$parsedSource) { + $$parsedSource["data"] = $$createField5_0($$parsedSource["data"]); + } + return new NotificationOptions($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = NotificationAction.createFrom; +const $$createType1 = $Create.Array($$createType0); +const $$createType2 = $Create.Map($Create.Any, $Create.Any); diff --git a/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/notificationservice.ts b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/notificationservice.ts new file mode 100644 index 000000000..859f3570f --- /dev/null +++ b/v3/examples/notifications/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/notifications/notificationservice.ts @@ -0,0 +1,62 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service represents the notifications service + * @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"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function CheckNotificationAuthorization(): $CancellablePromise { + return $Call.ByID(2216952893); +} + +export function RegisterNotificationCategory(category: $models.NotificationCategory): $CancellablePromise { + return $Call.ByID(2917562919, category); +} + +export function RemoveAllDeliveredNotifications(): $CancellablePromise { + return $Call.ByID(3956282340); +} + +export function RemoveAllPendingNotifications(): $CancellablePromise { + return $Call.ByID(108821341); +} + +export function RemoveDeliveredNotification(identifier: string): $CancellablePromise { + return $Call.ByID(975691940, identifier); +} + +export function RemoveNotification(identifier: string): $CancellablePromise { + return $Call.ByID(3966653866, identifier); +} + +export function RemoveNotificationCategory(categoryID: string): $CancellablePromise { + return $Call.ByID(2032615554, categoryID); +} + +export function RemovePendingNotification(identifier: string): $CancellablePromise { + return $Call.ByID(3729049703, identifier); +} + +/** + * Public methods that delegate to the implementation. + */ +export function RequestNotificationAuthorization(): $CancellablePromise { + return $Call.ByID(3933442950); +} + +export function SendNotification(options: $models.NotificationOptions): $CancellablePromise { + return $Call.ByID(3968228732, options); +} + +export function SendNotificationWithActions(options: $models.NotificationOptions): $CancellablePromise { + return $Call.ByID(1886542847, options); +} diff --git a/v3/examples/notifications/frontend/dist/Inter-Medium.ttf b/v3/examples/notifications/frontend/dist/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/notifications/frontend/dist/Inter-Medium.ttf differ diff --git a/v3/examples/notifications/frontend/dist/assets/index-Dat4utuQ.js b/v3/examples/notifications/frontend/dist/assets/index-Dat4utuQ.js new file mode 100644 index 000000000..b1c054dfb --- /dev/null +++ b/v3/examples/notifications/frontend/dist/assets/index-Dat4utuQ.js @@ -0,0 +1,33 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(o){if(o.ep)return;o.ep=!0;const i=n(o);fetch(o.href,i)}})();const fe="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function oe(t=21){let e="",n=t|0;for(;n--;)e+=fe[Math.random()*64|0];return e}const pe=window.location.origin+"/wails/runtime",x=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let we=oe();function C(t,e=""){return function(n,r=null){return he(t,n,e,r)}}async function he(t,e,n,r){var o,i;let s=new URL(pe);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),r&&s.searchParams.append("args",JSON.stringify(r));let a={"x-wails-client-id":we};n&&(a["x-wails-window-name"]=n);let c=await fetch(s,{headers:a});if(!c.ok)throw new Error(await c.text());return((i=(o=c.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?c.json():c.text()}C(x.System);const H=function(){var t,e,n,r,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(r=(n=window.webkit)===null||n===void 0?void 0:n.messageHandlers)===null||r===void 0?void 0:r.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` +%c⚠️ Browser Environment Detected %c + +%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. +More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development +`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function T(t){H==null||H(t)}function ie(){return window._wails.environment.OS==="windows"}function me(){return!!window._wails.environment.Debug}function ye(){return new MouseEvent("mousedown").buttons===0}function se(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",je);const ge=C(x.ContextMenu),be=0;function ve(t,e,n,r){ge(be,{id:t,x:e,y:n,data:r})}function je(t){const e=se(t),n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(n){t.preventDefault();const r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");ve(n,t.clientX,t.clientY,r)}else Ee(t,e)}function Ee(t,e){if(me())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const n=window.getSelection(),r=n&&n.toString().length>0;if(r)for(let o=0;o{F=t,F||(j=E=!1,u())};window.addEventListener("mousedown",Y,{capture:!0});window.addEventListener("mousemove",Y,{capture:!0});window.addEventListener("mouseup",Y,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,ze,{capture:!0});function ze(t){(S||E)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const I=0,Se=1,U=2;function Y(t){let e,n=t.buttons;switch(t.type){case"mousedown":e=I,P||(n=y|1<"u"||typeof e=="object"))try{var n=O.call(e);return(n===Ne||n===De||n===He||n===Re)&&e("")==null}catch{}return!1})}function Ue(t){if(V(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{b(t,null,_)}catch(e){if(e!==R)return!1}return!$(t)&&B(t)}function qe(t){if(V(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(Ae)return B(t);if($(t))return!1;var e=O.call(t);return e!==Oe&&e!==Te&&!/^\[object HTML/.test(e)?!1:B(t)}const m=b?Ue:qe;var q;class W extends Error{constructor(e,n){super(e,n),this.name="CancelError"}}class M extends Error{constructor(e,n,r){super((r??"Unhandled rejection in cancelled promise.")+" Reason: "+Fe(n),{cause:n}),this.promise=e,this.name="CancelledRejectionError"}}const p=Symbol("barrier"),G=Symbol("cancelImpl"),K=(q=Symbol.species)!==null&&q!==void 0?q:Symbol("speciesPolyfill");class l extends Promise{constructor(e,n){let r,o;if(super((c,f)=>{r=c,o=f}),this.constructor[K]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:r,reject:o,get oncancelled(){return n??null},set oncancelled(c){n=c??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[p]:{configurable:!1,enumerable:!1,writable:!0,value:null},[G]:{configurable:!1,enumerable:!1,writable:!1,value:ae(i,s)}});const a=ue(i,s);try{e(le(i,s),a)}catch(c){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",c):a(c)}}cancel(e){return new l(n=>{Promise.all([this[G](new W("Promise cancelled.",{cause:e})),_e(this)]).then(()=>n(),()=>n())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,n,r){if(!(this instanceof l))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(m(e)||(e=Q),m(n)||(n=Z),e===Q&&n==Z)return new l(i=>i(this));const o={};return this[p]=o,new l((i,s)=>{super.then(a=>{var c;this[p]===o&&(this[p]=null),(c=o.resolve)===null||c===void 0||c.call(o);try{i(e(a))}catch(f){s(f)}},a=>{var c;this[p]===o&&(this[p]=null),(c=o.resolve)===null||c===void 0||c.call(o);try{i(n(a))}catch(f){s(f)}})},async i=>{try{return r==null?void 0:r(i)}finally{await this.cancel(i)}})}catch(e,n){return this.then(void 0,e,n)}finally(e,n){if(!(this instanceof l))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return m(e)?this.then(r=>l.resolve(e()).then(()=>r),r=>l.resolve(e()).then(()=>{throw r}),n):this.then(e,e,n)}static get[K](){return Promise}static all(e){let n=Array.from(e);const r=n.length===0?l.resolve(n):new l((o,i)=>{Promise.all(n).then(o,i)},o=>L(r,n,o));return r}static allSettled(e){let n=Array.from(e);const r=n.length===0?l.resolve(n):new l((o,i)=>{Promise.allSettled(n).then(o,i)},o=>L(r,n,o));return r}static any(e){let n=Array.from(e);const r=n.length===0?l.resolve(n):new l((o,i)=>{Promise.any(n).then(o,i)},o=>L(r,n,o));return r}static race(e){let n=Array.from(e);const r=new l((o,i)=>{Promise.race(n).then(o,i)},o=>L(r,n,o));return r}static cancel(e){const n=new l(()=>{});return n.cancel(e),n}static timeout(e,n){const r=new l(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void r.cancel(n)):setTimeout(()=>void r.cancel(n),e),r}static sleep(e,n){return new l(r=>{setTimeout(()=>r(n),e)})}static reject(e){return new l((n,r)=>r(e))}static resolve(e){return e instanceof l?e:new l(n=>n(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new l((n,r)=>{e.resolve=n,e.reject=r},n=>{var r;(r=e.oncancelled)===null||r===void 0||r.call(e,n)}),e}}function ae(t,e){let n;return r=>{if(e.settled||(e.settled=!0,e.reason=r,t.reject(r),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==r)throw o})),!(!e.reason||!t.oncancelled))return n=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new M(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new M(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,n}}function le(t,e){return n=>{if(!e.resolving){if(e.resolving=!0,n===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(n!=null&&(typeof n=="object"||typeof n=="function")){let r;try{r=n.then}catch(o){e.settled=!0,t.reject(o);return}if(m(r)){try{let s=n.cancel;if(m(s)){const a=c=>{Reflect.apply(s,n,[c])};e.reason?ae(Object.assign(Object.assign({},t),{oncancelled:a}),e)(e.reason):t.oncancelled=a}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=ue(t,o);try{Reflect.apply(r,n,[le(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(n))}}}function ue(t,e){return n=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(n instanceof W&&e.reason instanceof W&&Object.is(n.cause,e.reason.cause))return}catch{}Promise.reject(new M(t.promise,n))}else e.settled=!0,t.reject(n)}}function L(t,e,n){const r=[];for(const o of e){let i;try{if(!m(o.then)||(i=o.cancel,!m(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[n])}catch(a){Promise.reject(new M(t,a,"Unhandled exception in cancel method."));continue}s&&r.push((s instanceof Promise?s:Promise.resolve(s)).catch(a=>{Promise.reject(new M(t,a,"Unhandled rejection in cancel method."))}))}return Promise.all(r)}function Q(t){return t}function Z(t){throw t}function Fe(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function _e(t){var e;let n=(e=t[p])!==null&&e!==void 0?e:{};return"promise"in n||Object.assign(n,g()),t[p]==null&&(n.resolve(),t[p]=n),n.promise}let g=Promise.withResolvers;g&&typeof g=="function"?g=g.bind(Promise):g=function(){let t,e;return{promise:new Promise((r,o)=>{t=r,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Xe;window._wails.callErrorHandler=Je;const Be=C(x.Call),We=C(x.CancelCall),v=new Map,Ye=0,$e=0;class Ve extends Error{constructor(e,n){super(e,n),this.name="RuntimeError"}}function Xe(t,e,n){const r=de(t);if(r)if(!e)r.resolve(void 0);else if(!n)r.resolve(e);else try{r.resolve(JSON.parse(e))}catch(o){r.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function Je(t,e,n){const r=de(t);if(r)if(!n)r.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(a){r.reject(new TypeError("could not parse error: "+a.message,{cause:a}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new Ve(o.message,i);break;default:s=new Error(o.message,i);break}r.reject(s)}}function de(t){const e=v.get(t);return v.delete(t),e}function Ge(){let t;do t=oe();while(v.has(t));return t}function Ke(t){const e=Ge(),n=l.withResolvers();v.set(e,{resolve:n.resolve,reject:n.reject});const r=Be(Ye,Object.assign({"call-id":e},t));let o=!1;r.then(()=>{o=!0},s=>{v.delete(e),n.reject(s)});const i=()=>(v.delete(e),We($e,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return n.oncancelled=()=>o?i():r.then(i),n.promise}function k(t,...e){return Ke({methodID:t,args:e})}const w=new Map;class Qe{constructor(e,n,r){this.eventName=e,this.callback=n,this.maxCallbacks=r||-1}dispatch(e){try{this.callback(e)}catch(n){console.error(n)}return this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0)}}function Ze(t){let e=w.get(t.eventName);e&&(e=e.filter(n=>n!==t),e.length===0?w.delete(t.eventName):w.set(t.eventName,e))}window._wails=window._wails||{};window._wails.dispatchWailsEvent=tt;C(x.Events);class et{constructor(e,n=null){this.name=e,this.data=n}}function tt(t){let e=w.get(t.name);if(!e)return;let n=new et(t.name,t.data);"sender"in t&&(n.sender=t.sender),e=e.filter(r=>!r.dispatch(n)),e.length===0?w.delete(t.name):w.set(t.name,e)}function nt(t,e,n){let r=w.get(t)||[];const o=new Qe(t,e,n);return r.push(o),w.set(t,r),()=>Ze(o)}function rt(t,e){return nt(t,e,-1)}window._wails=window._wails||{};window._wails.invoke=T;T("wails:runtime:ready");function X(){return k(2216952893)}function ot(t){return k(2917562919,t)}function it(){return k(3933442950)}function st(t){return k(3968228732,t)}function ct(t){return k(1886542847,t)}const d=document.querySelector("#response");var ee;(ee=document.querySelector("#request"))==null||ee.addEventListener("click",async()=>{try{await it()?(d&&(d.innerHTML="

Notifications are now authorized.

"),console.info("Notifications are now authorized.")):(d&&(d.innerHTML="

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"),console.warn(`Notifications are not authorized. + You can attempt to request again or let the user know in the UI. +`))}catch(t){console.error(t)}});var te;(te=document.querySelector("#check"))==null||te.addEventListener("click",async()=>{try{await X()?(d&&(d.innerHTML="

Notifications are authorized.

"),console.info("Notifications are authorized.")):(d&&(d.innerHTML="

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"),console.warn(`Notifications are not authorized. + You can attempt to request again or let the user know in the UI. +`))}catch(t){console.error(t)}});var ne;(ne=document.querySelector("#basic"))==null||ne.addEventListener("click",async()=>{try{await X()?await st({id:crypto.randomUUID(),title:"Notification Title",subtitle:"Subtitle on macOS and Linux",body:"Body text of notification.",data:{"user-id":"user-123","message-id":"msg-123",timestamp:Date.now()}}):(d&&(d.innerHTML="

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"),console.warn(`Notifications are not authorized. + You can attempt to request again or let the user know in the UI. +`))}catch(t){console.error(t)}});var re;(re=document.querySelector("#complex"))==null||re.addEventListener("click",async()=>{try{if(await X()){const e="frontend-notification-id";await ot({id:e,actions:[{id:"VIEW",title:"View"},{id:"MARK_READ",title:"Mark as read"},{id:"DELETE",title:"Delete",destructive:!0}],hasReplyField:!0,replyPlaceholder:"Message...",replyButtonTitle:"Reply"}),await ct({id:crypto.randomUUID(),title:"Notification Title",subtitle:"Subtitle on macOS and Linux",body:"Body text of notification.",categoryId:e,data:{"user-id":"user-123","message-id":"msg-123",timestamp:Date.now()}})}else d&&(d.innerHTML="

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"),console.warn(`Notifications are not authorized. + You can attempt to request again or let the user know in the UI. +`)}catch(t){console.error(t)}});const at=rt("notification:action",t=>{console.info(`Recieved a ${t.name} event`);const{userInfo:e,...n}=t.data[0];console.info("Notification Response:"),console.table(n),console.info("Notification Response Metadata:"),console.table(e);const r=` +
Notification Response
+ + + ${Object.keys(n).map(i=>``).join("")} + + + ${Object.values(n).map(i=>``).join("")} + +
${i}
${i}
+
Notification Metadata
+ + + ${Object.keys(e).map(i=>``).join("")} + + + ${Object.values(e).map(i=>``).join("")} + +
${i}
${i}
+ `,o=document.querySelector("#response");o&&(o.innerHTML=r)});window.onbeforeunload=()=>at(); diff --git a/v3/examples/notifications/frontend/dist/index.html b/v3/examples/notifications/frontend/dist/index.html new file mode 100644 index 000000000..b7794f4d8 --- /dev/null +++ b/v3/examples/notifications/frontend/dist/index.html @@ -0,0 +1,32 @@ + + + + + + + + Wails App + + + +
+ +

Wails + Typescript + Desktop Notifications

+

Send notifications 👇

+
+ + + + +
+ +
+ + diff --git a/v3/examples/notifications/frontend/dist/style.css b/v3/examples/notifications/frontend/dist/style.css new file mode 100644 index 000000000..074717bca --- /dev/null +++ b/v3/examples/notifications/frontend/dist/style.css @@ -0,0 +1,131 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +.controls { + display: flex; + gap: 1em; +} + +button { + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3em; +} + +h1, h3 { + line-height: 1.1; + text-align: center; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +.footer table { + font-size: 12px; + border-collapse: collapse; + margin: 0 auto; +} + +.footer table, th, td { + border: 1px solid #ddd; + padding: 0.5em; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} \ No newline at end of file diff --git a/v3/examples/notifications/frontend/dist/typescript.svg b/v3/examples/notifications/frontend/dist/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/notifications/frontend/dist/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/notifications/frontend/dist/wails.png b/v3/examples/notifications/frontend/dist/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/notifications/frontend/dist/wails.png differ diff --git a/v3/examples/notifications/frontend/index.html b/v3/examples/notifications/frontend/index.html new file mode 100644 index 000000000..b873cd4f3 --- /dev/null +++ b/v3/examples/notifications/frontend/index.html @@ -0,0 +1,32 @@ + + + + + + + + Wails App + + +
+ +

Wails + Typescript + Desktop Notifications

+

Send notifications 👇

+
+ + + + +
+ +
+ + + diff --git a/v3/examples/notifications/frontend/package.json b/v3/examples/notifications/frontend/package.json new file mode 100644 index 000000000..b39da7ece --- /dev/null +++ b/v3/examples/notifications/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^5.0.0" + } +} diff --git a/v3/examples/notifications/frontend/public/Inter-Medium.ttf b/v3/examples/notifications/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/notifications/frontend/public/Inter-Medium.ttf differ diff --git a/v3/examples/notifications/frontend/public/style.css b/v3/examples/notifications/frontend/public/style.css new file mode 100644 index 000000000..074717bca --- /dev/null +++ b/v3/examples/notifications/frontend/public/style.css @@ -0,0 +1,131 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +.controls { + display: flex; + gap: 1em; +} + +button { + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3em; +} + +h1, h3 { + line-height: 1.1; + text-align: center; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +.footer table { + font-size: 12px; + border-collapse: collapse; + margin: 0 auto; +} + +.footer table, th, td { + border: 1px solid #ddd; + padding: 0.5em; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} \ No newline at end of file diff --git a/v3/examples/notifications/frontend/public/typescript.svg b/v3/examples/notifications/frontend/public/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/notifications/frontend/public/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/notifications/frontend/public/wails.png b/v3/examples/notifications/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/notifications/frontend/public/wails.png differ diff --git a/v3/examples/notifications/frontend/src/main.ts b/v3/examples/notifications/frontend/src/main.ts new file mode 100644 index 000000000..94bbb7df3 --- /dev/null +++ b/v3/examples/notifications/frontend/src/main.ts @@ -0,0 +1,129 @@ +import { Events } from "@wailsio/runtime"; +import { NotificationService } from "../bindings/github.com/wailsapp/wails/v3/pkg/services/notifications"; + +const footer = document.querySelector("#response"); + +document.querySelector("#request")?.addEventListener("click", async () => { + try { + const authorized = await NotificationService.RequestNotificationAuthorization(); + if (authorized) { + if (footer) footer.innerHTML = "

Notifications are now authorized.

"; + console.info("Notifications are now authorized."); + } else { + if (footer) footer.innerHTML = "

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"; + console.warn("Notifications are not authorized.\n You can attempt to request again or let the user know in the UI.\n"); + } + } catch (error) { + console.error(error); + } +}); + +document.querySelector("#check")?.addEventListener("click", async () => { + try { + const authorized = await NotificationService.CheckNotificationAuthorization(); + if (authorized) { + if (footer) footer.innerHTML = "

Notifications are authorized.

"; + console.info("Notifications are authorized."); + } else { + if (footer) footer.innerHTML = "

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"; + console.warn("Notifications are not authorized.\n You can attempt to request again or let the user know in the UI.\n"); + } + } catch (error) { + console.error(error); + } +}); + +document.querySelector("#basic")?.addEventListener("click", async () => { + try { + const authorized = await NotificationService.CheckNotificationAuthorization(); + if (authorized) { + await NotificationService.SendNotification({ + id: crypto.randomUUID(), + title: "Notification Title", + subtitle: "Subtitle on macOS and Linux", + body: "Body text of notification.", + data: { + "user-id": "user-123", + "message-id": "msg-123", + "timestamp": Date.now(), + }, + }); + } else { + if (footer) footer.innerHTML = "

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"; + console.warn("Notifications are not authorized.\n You can attempt to request again or let the user know in the UI.\n"); + } + } catch (error) { + console.error(error); + } +}); +document.querySelector("#complex")?.addEventListener("click", async () => { + try { + const authorized = await NotificationService.CheckNotificationAuthorization(); + if (authorized) { + const CategoryID = "frontend-notification-id"; + + await NotificationService.RegisterNotificationCategory({ + id: CategoryID, + actions: [ + { id: "VIEW", title: "View" }, + { id: "MARK_READ", title: "Mark as read" }, + { id: "DELETE", title: "Delete", destructive: true }, + ], + hasReplyField: true, + replyPlaceholder: "Message...", + replyButtonTitle: "Reply", + }); + + await NotificationService.SendNotificationWithActions({ + id: crypto.randomUUID(), + title: "Notification Title", + subtitle: "Subtitle on macOS and Linux", + body: "Body text of notification.", + categoryId: CategoryID, + data: { + "user-id": "user-123", + "message-id": "msg-123", + "timestamp": Date.now(), + }, + }); + } else { + if (footer) footer.innerHTML = "

Notifications are not authorized. You can attempt to request again or let the user know in the UI.

"; + console.warn("Notifications are not authorized.\n You can attempt to request again or let the user know in the UI.\n"); + } + } catch (error) { + console.error(error); + } +}); + +const unlisten = Events.On("notification:action", (response) => { + console.info(`Recieved a ${response.name} event`); + const { userInfo, ...base } = response.data[0]; + console.info("Notification Response:"); + console.table(base); + console.info("Notification Response Metadata:"); + console.table(userInfo); + const table = ` +
Notification Response
+ + + ${Object.keys(base).map(key => ``).join("")} + + + ${Object.values(base).map(value => ``).join("")} + +
${key}
${value}
+
Notification Metadata
+ + + ${Object.keys(userInfo).map(key => ``).join("")} + + + ${Object.values(userInfo).map(value => ``).join("")} + +
${key}
${value}
+ `; + const footer = document.querySelector("#response"); + if (footer) footer.innerHTML = table; +}); + +window.onbeforeunload = () => unlisten(); \ No newline at end of file diff --git a/v3/examples/notifications/frontend/src/vite-env.d.ts b/v3/examples/notifications/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/examples/notifications/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/examples/notifications/frontend/tsconfig.json b/v3/examples/notifications/frontend/tsconfig.json new file mode 100644 index 000000000..c267ecf24 --- /dev/null +++ b/v3/examples/notifications/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/v3/examples/notifications/go.mod b/v3/examples/notifications/go.mod new file mode 100644 index 000000000..ebd5f4fc0 --- /dev/null +++ b/v3/examples/notifications/go.mod @@ -0,0 +1,53 @@ +module notifications + +go 1.24.0 + +toolchain go1.24.1 + +require github.com/wailsapp/wails/v3 v3.0.0-dev + +require ( + dario.cat/mergo v1.0.1 // indirect + git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) + +replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/notifications/go.sum b/v3/examples/notifications/go.sum new file mode 100644 index 000000000..6f8b2a29a --- /dev/null +++ b/v3/examples/notifications/go.sum @@ -0,0 +1,148 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +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/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/v3/examples/notifications/main.go b/v3/examples/notifications/main.go new file mode 100644 index 000000000..c0e006652 --- /dev/null +++ b/v3/examples/notifications/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "embed" + _ "embed" + "fmt" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/services/notifications" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed all:frontend/dist +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + // Create a new Notification Service + ns := notifications.New() + + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + app := application.New(application.Options{ + Name: "Notifications Demo", + Description: "A demo of using desktop notifications with Wails", + Services: []application.Service{ + application.NewService(ns), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Name: "main", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + // Pass a notification callback that will be called when a notification is actioned. + ns.OnNotificationResponse(func(result notifications.NotificationResult) { + if result.Error != nil { + println(fmt.Errorf("parsing notification result failed: %s", result.Error)) + } else { + fmt.Printf("Response: %+v\n", result.Response) + println("Sending response to frontend...") + app.Event.Emit("notification:action", result.Response) + } + }) + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/panic-handling/README.md b/v3/examples/panic-handling/README.md new file mode 100644 index 000000000..99068495f --- /dev/null +++ b/v3/examples/panic-handling/README.md @@ -0,0 +1,11 @@ +# Panic Handling Example + +This example is a demonstration of how to handle panics in your application. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` diff --git a/v3/examples/panic-handling/assets/index.html b/v3/examples/panic-handling/assets/index.html new file mode 100644 index 000000000..f4b5fe886 --- /dev/null +++ b/v3/examples/panic-handling/assets/index.html @@ -0,0 +1,27 @@ + + + + + + Window Call Demo + + + + + + + + + \ No newline at end of file diff --git a/v3/examples/panic-handling/main.go b/v3/examples/panic-handling/main.go new file mode 100644 index 000000000..cb19165be --- /dev/null +++ b/v3/examples/panic-handling/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "embed" + "fmt" + "github.com/wailsapp/wails/v3/pkg/application" + "log" +) + +//go:embed assets/* +var assets embed.FS + +type WindowService struct{} + +func (s *WindowService) GeneratePanic() { + s.call1() +} + +func (s *WindowService) call1() { + s.call2() +} + +func (s *WindowService) call2() { + panic("oh no! something went wrong deep in my service! :(") +} + +// ============================================== + +func main() { + app := application.New(application.Options{ + Name: "Panic Handler Demo", + Description: "A demo of Handling Panics", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: false, + }, + Services: []application.Service{ + application.NewService(&WindowService{}), + }, + PanicHandler: func(panicDetails *application.PanicDetails) { + fmt.Printf("*** There was a panic! ***\n") + fmt.Printf("Time: %s\n", panicDetails.Time) + fmt.Printf("Error: %s\n", panicDetails.Error) + fmt.Printf("Stacktrace: %s\n", panicDetails.StackTrace) + application.InfoDialog().SetMessage("There was a panic!").Show() + }, + }) + + app.Window.New(). + SetTitle("WebviewWindow 1"). + Show() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/plain/README.md b/v3/examples/plain/README.md new file mode 100644 index 000000000..9d44f5119 --- /dev/null +++ b/v3/examples/plain/README.md @@ -0,0 +1,19 @@ +# Plain Example + +This example is a demonstration of different ways to create applications without using npm. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/plain/main.go b/v3/examples/plain/main.go new file mode 100644 index 000000000..3132d65b8 --- /dev/null +++ b/v3/examples/plain/main.go @@ -0,0 +1,84 @@ +package main + +import ( + _ "embed" + "log" + "net/http" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Plain", + Description: "A demo of using raw HTML & CSS", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`Plain Bundle

Plain Bundle

This is a plain bundle. It has no frontend code but this was Served by the AssetServer's Handler.



Clicking this paragraph emits an event...

`)) + }), + }, + }) + // Create window - Note: In future versions, window creation may return errors + // that should be checked. For now, window creation is deferred until app.Run() + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Plain Bundle", + CSS: `body { background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`, + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + }, + URL: "/", + }) + + // Create second window with direct HTML content + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "HTML TEST", + HTML: "

AWESOME!

", + CSS: `body { background-color: rgb(255, 0, 0); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`, + JS: `window.iamhere = function() { console.log("Hello World!"); }`, + }) + + // Store the cleanup function to remove event listener when needed + removeClickHandler := app.Event.On("clicked", func(_ *application.CustomEvent) { + println("clicked") + }) + // Note: In a real application, you would call removeClickHandler() when appropriate + _ = removeClickHandler // Acknowledge we're storing the cleanup function + + // Use context-aware goroutine for graceful shutdown + go func() { + // Use a ticker instead of sleep to allow for cancellation + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + select { + case <-ticker.C: + // Create window after delay - in production, you should handle potential errors + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Plain Bundle new Window from GoRoutine", + Width: 500, + Height: 500, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + case <-app.Context().Done(): + // Application is shutting down, cancel the goroutine + return + } + }() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/raw-message/README.md b/v3/examples/raw-message/README.md new file mode 100644 index 000000000..8d28d4f8a --- /dev/null +++ b/v3/examples/raw-message/README.md @@ -0,0 +1,11 @@ +# Raw Message Example + +This example is a demonstration of sending raw messages from JS to Go. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run main.go +``` diff --git a/v3/examples/raw-message/assets/index.html b/v3/examples/raw-message/assets/index.html new file mode 100644 index 000000000..52f174336 --- /dev/null +++ b/v3/examples/raw-message/assets/index.html @@ -0,0 +1,25 @@ + + + + + Title + + + + +

Raw Message Demo

+
+To send a raw message from this window, enter some text and click the button: +
+
+ + + + diff --git a/v3/examples/raw-message/main.go b/v3/examples/raw-message/main.go new file mode 100644 index 000000000..cd879ea5c --- /dev/null +++ b/v3/examples/raw-message/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "embed" + _ "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "log" +) + +//go:embed assets +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Raw Message Demo", + Description: "A demo of sending raw messages from the frontend", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + RawMessageHandler: func(window application.Window, message string) { + println("Raw message received from Window '" + window.Name() + "' with message: " + message) + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Name: "Window 1", + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/screen/README.md b/v3/examples/screen/README.md new file mode 100644 index 000000000..1f922f341 --- /dev/null +++ b/v3/examples/screen/README.md @@ -0,0 +1,19 @@ +# Screen Example + +This example will detect all attached screens and display their details. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/screen/assets/examples.js b/v3/examples/screen/assets/examples.js new file mode 100644 index 000000000..8abe3f0ca --- /dev/null +++ b/v3/examples/screen/assets/examples.js @@ -0,0 +1,206 @@ +window.examples = [ + [ + // Normal examples (demonstrate real life scenarios) + { + name: "Single 4k monitor", + screens: [ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + ] + }, + { + name: "Two monitors", + screens: [ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: {id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + ] + }, + { + name: "Two monitors (2)", + screens: [ + {id: 1, w: 1920, h: 1080, s: 1, name: `23" FHD 96DPI`}, + {id: 2, w: 1920, h: 1080, s: 1.25, parent: {id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI (125%)`}, + ] + }, + { + name: "Three monitors", + screens: [ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: {id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + {id: 3, w: 1920, h: 1080, s: 1.25, parent: {id: 1, align: "l", offset: 0}, name: `23" FHD 96DPI (125%)`}, + ] + }, + { + name: "Four monitors", + screens: [ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: {id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + {id: 3, w: 1920, h: 1080, s: 1.25, parent: {id: 2, align: "b", offset: 0}, name: `23" FHD 96DPI (125%)`}, + {id: 4, w: 1080, h: 1920, s: 1, parent: {id: 1, align: "l", offset: 0}, name: `23" FHD (90deg)`}, + ] + }, + ], + [ + // Test cases examples (demonstrate the algorithm basics) + { + name: "Child scaled, Start offset", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1.5, parent: {id: 1, align: "r", offset: 600}, name: "Child"}, + ] + }, + { + name: "Child scaled, End offset", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1.5, parent: {id: 1, align: "r", offset: -600}, name: "Child"}, + ] + }, + { + name: "Parent scaled, Start offset percent", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1, parent: {id: 1, align: "r", offset: 600}, name: "Child"}, + ] + }, + { + name: "Parent scaled, End offset percent", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1, parent: {id: 1, align: "r", offset: -600}, name: "Child"}, + ] + }, + { + name: "Parent scaled, Start align", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1100, s: 1, parent: {id: 1, align: "r", offset: 0}, name: "Child"}, + ] + }, + { + name: "Parent scaled, End align", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1, parent: {id: 1, align: "r", offset: 0}, name: "Child"}, + ] + }, + { + name: "Parent scaled, in-between", + screens: [ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1500, s: 1, parent: {id: 1, align: "r", offset: -200}, name: "Child"}, + ] + }, + ], + [ + // Edge cases examples + { + name: "Parent order (5 is parent of 4)", + screens: [ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 600, s: 1.25, parent: {id: 1, align: "r", offset: -200}}, + {id: 3, w: 800, h: 800, s: 1.25, parent: {id: 2, align: "b", offset: 0}}, + {id: 4, w: 800, h: 1080, s: 1.5, parent: {id: 2, align: "re", offset: 100}}, + {id: 5, w: 600, h: 600, s: 1, parent: {id: 3, align: "r", offset: 100}}, + ] + }, + { + name: "de-intersection reparent", + screens: [ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1680, h: 1050, s: 1.25, parent: {id: 1, align: "r", offset: 10}}, + {id: 3, w: 1440, h: 900, s: 1.5, parent: {id: 1, align: "le", offset: 150}}, + {id: 4, w: 1024, h: 768, s: 1, parent: {id: 3, align: "bc", offset: -200}}, + {id: 5, w: 1024, h: 768, s: 1.25, parent: {id: 4, align: "r", offset: 400}}, + ] + }, + { + name: "de-intersection (unattached child)", + screens: [ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 768, s: 1.5, parent: {id: 1, align: "le", offset: 10}}, + {id: 3, w: 1024, h: 768, s: 1.25, parent: {id: 2, align: "b", offset: 100}}, + {id: 4, w: 1024, h: 768, s: 1, parent: {id: 3, align: "r", offset: 500}}, + ] + }, + { + name: "Multiple de-intersection", + screens: [ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 768, s: 1, parent: {id: 1, align: "be", offset: 0}}, + {id: 3, w: 1024, h: 768, s: 1, parent: {id: 2, align: "b", offset: 300}}, + {id: 4, w: 1024, h: 768, s: 1.5, parent: {id: 2, align: "le", offset: 100}}, + {id: 5, w: 1024, h: 768, s: 1, parent: {id: 4, align: "be", offset: 100}}, + ] + }, + { + name: "Multiple de-intersection (left-side)", + screens: [ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 768, s: 1, parent: {id: 1, align: "le", offset: 0}}, + {id: 3, w: 1024, h: 768, s: 1, parent: {id: 2, align: "b", offset: 300}}, + {id: 4, w: 1024, h: 768, s: 1.5, parent: {id: 2, align: "le", offset: 100}}, + {id: 5, w: 1024, h: 768, s: 1, parent: {id: 4, align: "be", offset: 100}}, + ] + }, + { + name: "Parent de-intersection child offset", + screens: [ + {id: 1, w: 1600, h: 1600, s: 1.5}, + {id: 2, w: 800, h: 800, s: 1, parent: {id: 1, align: "r", offset: 0}}, + {id: 3, w: 800, h: 800, s: 1, parent: {id: 1, align: "r", offset: 800}}, + {id: 4, w: 800, h: 1600, s: 1, parent: {id: 2, align: "r", offset: 0}}, + ] + }, + ], +].map(sections => sections.map(layout => { + return parseLayout(layout) +})) + +function parseLayout(layout) { + const screens = [] + + for (const screen of layout.screens) { + let x = 0, y = 0 + const {w, h} = screen + + if (screen.parent) { + const parent = screens.find(s => s.ID == screen.parent.id).Bounds + const offset = screen.parent.offset + let align = screen.parent.align + let align2 = "" + + if (align.length == 2) { + align2 = align.charAt(1) + align = align.charAt(0) + } + + x = parent.X + y = parent.Y + // t: top, b: bottom, l: left, r: right, e: edge, c: corner + if (align == "t" || align == "b") { + x += offset + (align2 == "e" || align2 == "c" ? parent.Width : 0) - (align2 == "e" ? w : 0) + y += (align == "t" ? -h : parent.Height) + } else { + y += offset + (align2 == "e" || align2 == "c" ? parent.Height : 0) - (align2 == "e" ? h : 0) + x += (align == "l" ? -w : parent.Width) + } + } + + screens.push({ + ID: `${screen.id}`, + Name: screen.name ?? `Display${screen.id}`, + ScaleFactor: Math.round(screen.s * 100) / 100, + X: x, + Y: y, + Size: {Width: w, Height: h}, + Bounds: {X: x, Y: y, Width: w, Height: h}, + PhysicalBounds: {X: x, Y: y, Width: w, Height: h}, + WorkArea: {X: x, Y: y, Width: w, Height: h-Math.round(40*screen.s)}, + PhysicalWorkArea: {X: x, Y: y, Width: w, Height: h-Math.round(40*screen.s)}, + IsPrimary: screen.id == 1, + Rotation: 0 + }) + } + + return {name: layout.name, screens} +} diff --git a/v3/examples/screen/assets/index.html b/v3/examples/screen/assets/index.html new file mode 100644 index 000000000..358624411 --- /dev/null +++ b/v3/examples/screen/assets/index.html @@ -0,0 +1,141 @@ + + + + + Screens Demo + + + +
+ + +
+ +  X: + + +  Width: + + +   + + +   + + +   + +   + Layers: +
+
+
+
+ Screens:  + System + +  - Examples + : +   + + +
+
+ Coordinates:  + Physical (PX) + Logical (DIP) + + +   + + + + + +
+
+ + + + + + +
+ + + + + + diff --git a/v3/examples/screen/assets/main.js b/v3/examples/screen/assets/main.js new file mode 100644 index 000000000..316c08002 --- /dev/null +++ b/v3/examples/screen/assets/main.js @@ -0,0 +1,406 @@ +setExamplesType(document.getElementById('examples-type').value, 0) + +function setExamplesType(type, autoSelectLayout = 1) { + window.examples_type = parseInt(type) + document.getElementById('examples-list').innerHTML = examples[examples_type].map((layout, i) => { + return `${i + 1}` + }).join("\n") + if (autoSelectLayout != null) setLayout(autoSelectLayout) +} + +async function setLayout(indexOrLayout, physicalCoordinate = true) { + if (typeof indexOrLayout == 'number') { + await radioBtnClick(null, `#layout-selector [data-value="${indexOrLayout}"]`) + } else { + document.querySelectorAll('#layout-selector .active').forEach(el => el.classList.remove('active')) + window.layout = indexOrLayout + window.point = null + window.rect = null + await processLayout() + await draw() + } + + const physical = !parseInt(document.querySelector('#coordinate-selector .active').dataset.value) + if (physical != physicalCoordinate) { + await setCoordinateType(physicalCoordinate) + } +} + +async function setCoordinateType(physicalCoordinate = true) { + await radioBtnClick(null, `#coordinate-selector [data-value="${physicalCoordinate ? 0 : 1}"]`) +} + +async function radioBtnClick(e, selector) { + if (e == null) { + e = new Event("mousedown") + document.querySelector(selector).dispatchEvent(e) + } + if (!e.target.classList.contains('radio-btn')) return + const btnGroup = e.target.closest('.radio-btn-group') + btnGroup.querySelectorAll('.radio-btn.active').forEach(el => el.classList.remove('active')) + e.target.classList.add('active') + + if (btnGroup.id == 'layout-selector') { + window.point = null + window.rect = null + await processLayout() + } + + await draw() +} + +async function processLayout() { + const layoutBtn = document.querySelector('#layout-selector .active') + const i = layoutBtn ? parseInt(layoutBtn.dataset.value) : -1 + if (i == 0) { + // system screens + window.layout = { + name: '', + screens: await callBinding('main.ScreenService.GetSystemScreens'), + } + } else { + if (i > 0) { + // example layouts + window.layout = structuredClone(examples[examples_type][i - 1]) + } + layout.screens = await callBinding('main.ScreenService.ProcessExampleScreens', layout.screens) + } + document.getElementById('example-name').textContent = layout.name +} + +async function draw() { + console.log(layout) + let minX = 0, minY = 0, maxX = 0, maxY = 0; + let html = ''; + + const physical = !parseInt(document.querySelector('#coordinate-selector .active').dataset.value) + const retainViewbox = document.querySelector('#retain-viewbox').checked + + layout.screens.forEach(screen => { + const b = physical ? screen.PhysicalBounds : screen.Bounds + const wa = physical ? screen.PhysicalWorkArea : screen.WorkArea + const vbBounds = retainViewbox ? [screen.Bounds, screen.PhysicalBounds] : [b] + + minX = Math.min(minX, ...vbBounds.map(b => b.X)) + minY = Math.min(minY, ...vbBounds.map(b => b.Y)) + maxX = Math.max(maxX, ...vbBounds.map(b => b.X + b.Width)) + maxY = Math.max(maxY, ...vbBounds.map(b => b.Y + b.Height)) + + html += ` + + + + + + (${b.X}, ${b.Y}) + + ${screen.Name} + ${b.Width} x ${b.Height} + Scale factor: ${screen.ScaleFactor} + + + ` + }) + + const svg = document.getElementById('svg') + svg.innerHTML = ` + ${svg.querySelector('& > defs').outerHTML} + + ${html} + + + ` + + svg.setAttribute('viewBox', `${minX} ${minY} ${maxX - minX} ${maxY - minY}`) + + if (window.point) await probePoint() + if (window.rect) await drawRect() + + svg.onmousedown = async function(e) { + let pt = new DOMPoint(e.clientX, e.clientY) + pt = pt.matrixTransform(svg.getScreenCTM().inverse()) + pt.x = parseInt(pt.x) + pt.y = parseInt(pt.y) + if (e.buttons == 1) { + await probePoint({X: pt.x, Y: pt.y}) + } else if (e.buttons == 2) { + if (e.ctrlKey) { + if (!window.rect) { + window.rect = {X: pt.x, Y: pt.y, Width: 0, Height: 0} + } + if (!window.rectCursor) { + window.rectAnchor = {x: window.rect.X, y: window.rect.Y} + window.rectCursor = {x: window.rectAnchor.x + window.rect.Width, y: window.rectAnchor.y + window.rect.Height} + } + window.rectCursorOffset = { + x: pt.x - window.rectCursor.x, + y: pt.y - window.rectCursor.y, + } + } else { + window.rectAnchor = pt + window.rectCursorOffset = {x: 0, y: 0} + window.probing = true + drawRect({X: pt.x, Y: pt.y, Width: 0, Height: 0}) + window.probing = false + } + } else if (e.buttons == 4) { + drawRect({X: pt.x, Y: pt.y, Width: 50, Height: 50}) + } + } + svg.onmousemove = async function(e) { + if (window.probing) return + window.probing = true + if (e.buttons == 1) { + await svg.onmousedown(e) + } else if (e.buttons == 2) { + let pt = new DOMPoint(e.clientX, e.clientY) + pt = pt.matrixTransform(svg.getScreenCTM().inverse()) + if (e.ctrlKey) { + window.rectAnchor.x += pt.x - rectCursor.x - window.rectCursorOffset.x + window.rectAnchor.y += pt.y - rectCursor.y - window.rectCursorOffset.y + } + window.rectCursor = { + x: pt.x - window.rectCursorOffset.x, + y: pt.y - window.rectCursorOffset.y, + } + await drawRect({ + X: parseInt(Math.min(window.rectAnchor.x, window.rectCursor.x)), + Y: parseInt(Math.min(window.rectAnchor.y, window.rectCursor.y)), + Width: parseInt(Math.abs(window.rectCursor.x - window.rectAnchor.x)), + Height: parseInt(Math.abs(window.rectCursor.y - window.rectAnchor.y)), + }) + } + window.probing = false + } + svg.oncontextmenu = function(e) { + e.preventDefault() + } +} + +async function probePoint(p = null) { + const svg = document.getElementById('svg'); + const physical = !parseInt(document.querySelector('#coordinate-selector .active').dataset.value) + + if (p == null) { + if (window.pointIsPhysical == physical) { + p = window.point + } else { + p = (await callBinding('main.ScreenService.TransformPoint', window.point, window.pointIsPhysical))[0] + } + } + + window.point = p + window.pointIsPhysical = physical + const [ptTransformed, ptDblTransformed] = await callBinding('main.ScreenService.TransformPoint', p, physical) + + svg.getElementById('points').innerHTML = ` + + + + + ` + // await new Promise((resolve) => setTimeout(resolve, 200)) // delay + return ptDblTransformed +} + +async function drawRect(r = null) { + const svg = document.getElementById('svg'); + const physical = !parseInt(document.querySelector('#coordinate-selector .active').dataset.value) + + if (r == null) { + if (window.rectIsPhysical == physical) { + r = window.rect + } else { + r = await callBinding('main.ScreenService.TransformRect', window.rect, window.rectIsPhysical) + } + } + + if (!window.probing) { + window.rectAnchor = null + window.rectCursor = null + } + + document.getElementById('x').value = r.X + document.getElementById('y').value = r.Y + document.getElementById('w').value = r.Width + document.getElementById('h').value = r.Height + + window.rect = r + window.rectIsPhysical = physical + window.rTransformed = await callBinding('main.ScreenService.TransformRect', r, physical) + window.rDblTransformed = await callBinding('main.ScreenService.TransformRect', rTransformed, !physical) + window.rTransformed = rTransformed + + await rectLayers() + return rDblTransformed +} + +async function rectLayers() { + const s = document.getElementById('slider').value + if (window.rect == null) await test1() + + const r = await callBinding('main.ScreenService.TransformRect', rectIsPhysical ? rect : rTransformed, true) + const rShifted = {...r, X: r.X+50} + const rShiftedPhysical = await callBinding('main.ScreenService.TransformRect', rShifted, false) + + svg.getElementById('rects').innerHTML = [ + [window.rect, 'rgb(255 255 255 / 100%)'], // w + [window.rTransformed, 'rgb(0 255 0 / 25%)'], // g + [window.rDblTransformed, 'none'], // none + [rShifted, 'rgb(255 0 0 / 15%)'], // r + [rShiftedPhysical, 'rgb(0 0 255 / 15%)'], // b + ].filter((_,i) => i { + let lines = '' + if (i == 0) { + const center = {X: r.X + (r.Width-1)/2, Y: r.Y + (r.Height-1)/2} + lines += ` + + + ` + } + return `${lines}` + }).join('/n') +} + +async function updateDipRect(x, y=0, w=0, h=0) { + if (rect == null) { + await drawRect({ + X: +document.getElementById('x').value, + Y: +document.getElementById('y').value, + Width: +document.getElementById('w').value, + Height: +document.getElementById('h').value, + }) + } + // Simulate real window by first retrieving the physical bounds then transforming it to dip + // then updating the bounds and transforming it back to physical + let rPhysical = rectIsPhysical ? rect : rTransformed + const r = await callBinding('main.ScreenService.TransformRect', rPhysical, true) + r.X += x + r.Y += y + r.Width += w + r.Height += h + rPhysical = await callBinding('main.ScreenService.TransformRect', r, false) + drawRect(rectIsPhysical ? rPhysical : r) +} + +function arrowMove(e) { + let x = 0, y = 0 + if (e.key == 'ArrowLeft') x = -step.value + if (e.key == 'ArrowRight') x = +step.value + if (e.key == 'ArrowUp') y = -step.value + if (e.key == 'ArrowDown') y = +step.value + if (!(x || y)) return + e.preventDefault() + updateDipRect(x, y) +} + +async function test1() { + // Edge case 1: invalid dip rect: no physical rect can produce it + await setLayout(parseLayout({screens: [ + {id: 1, w: 1200, h: 1200, s: 1}, + {id: 2, w: 1200, h: 1100, s: 1.5, parent: {id: 1, align: "r", offset: 0}}, + ]}), false) + await drawRect({X: 1050, Y: 700, Width: 400, Height: 300}) +} + +async function test2() { + // Edge case 2: physical rect that changes when double transformed (2 physical rects produce the same dip rect) + await setLayout(parseLayout({screens: [ + {id: 1, w: 1200, h: 1200, s: 1.5}, + {id: 2, w: 1200, h: 900, s: 1, parent: {id: 1, align: "r", offset: 0}}, + ]}), true) + await drawRect({X: 1050, Y: 890, Width: 400, Height: 300}) +} + +async function probeLayout(finishup = true) { + const probeButtons = document.getElementById('probe-buttons') + const svg = document.getElementById('svg') + const threshold = 1 + + const physical = !parseInt(document.querySelector('#coordinate-selector .active').dataset.value) + window.cancelProbing = false + probeButtons.classList.add('active') + + const steps = 3 + let failed = false + for (const screen of layout.screens) { + if (window.cancelProbing) break + const b = physical ? screen.PhysicalBounds : screen.Bounds + const xStep = parseInt(b.Width / steps) || 1 + const yStep = parseInt(b.Height / steps) || 1 + let x = b.X, y = b.Y + let xDone = false, yDone = false + + while (!(yDone || window.cancelProbing)) { + if (y >= b.Y + b.Height - 1) { + y = b.Y + b.Height - 1 + yDone = true + } + x = b.X + xDone = false + while (!(xDone || window.cancelProbing)) { + if (x >= b.X + b.Width - 1) { + x = b.X + b.Width - 1 + xDone = true + } + const pt = {X: x, Y: y} + let ptDblTransformed, err + try { + ptDblTransformed = await probePoint(pt) + } catch (e) { + err = e + } + if (err || Math.abs(pt.X - ptDblTransformed.X) > threshold || Math.abs(pt.Y - ptDblTransformed.Y) > threshold) { + failed = true + console.log(pt, ptDblTransformed) + window.cancelProbing = true + setTimeout(() => { + alert(err ?? `**FAILED**\nProbing failed at point: {X: ${pt.X}, Y: ${pt.Y}}\nDouble transformed point: {X: ${ptDblTransformed.X}, Y: ${ptDblTransformed.Y}}\n(Exceeded threshold of ${threshold} pixels)`) + }, 50) + } + x += xStep + } + y += yStep + } + } + + if (finishup || window.cancelProbing) probeButtons.classList.remove('active') + if (!(failed || window.cancelProbing)) { + window.point = null + if (finishup) { + setTimeout(() => { + svg.getElementById('points').innerHTML = '' + alert(`Successfully probed all points!, All within threshold of ${threshold} pixels.`) + }, 50) + } + return true + } +} + +async function probeAllExamples() { + console.time('probeAllExamples') +loop1: + for (let typeI = 0; typeI < examples.length; typeI++) { + document.getElementById('examples-type').value = typeI + setExamplesType(typeI, null) + + for (let layoutI = (typeI ? 0 : -1); layoutI < examples[typeI].length; layoutI++) { + await radioBtnClick(null, `#layout-selector [data-value="${layoutI + 1}"]`) + for (let i = 0; i < 2; i++) { + const lastLayout = (typeI == examples.length - 1 && layoutI == examples[typeI].length - 1 && i == 1) + if (!await probeLayout(lastLayout)) break loop1 + if (i == 0) await setCoordinateType(!pointIsPhysical) + } + } + } + console.timeEnd('probeAllExamples') +} + +async function callBinding(name, ...params) { + return wails.Call.ByName(name, ...params) +} + +function showAdvanced(e) { + e.target.style.display = 'none' + document.querySelectorAll('.advanced').forEach(el => el.style.display = 'initial') +} diff --git a/v3/examples/screen/main.go b/v3/examples/screen/main.go new file mode 100644 index 000000000..75d0c8bd2 --- /dev/null +++ b/v3/examples/screen/main.go @@ -0,0 +1,78 @@ +package main + +import ( + "embed" + "log" + "log/slog" + "net/http" + "os" + "path/filepath" + "runtime" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Screen Demo", + Description: "A demo of the Screen API", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Windows: application.WindowsOptions{ + WndProcInterceptor: nil, + DisableQuitOnLastWindowClosed: false, + WebviewUserDataPath: "", + WebviewBrowserPath: "", + }, + Services: []application.Service{ + application.NewService(&ScreenService{}), + }, + LogLevel: slog.LevelError, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + Middleware: func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Disable caching + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + w.Header().Set("Pragma", "no-cache") + w.Header().Set("Expires", "0") + + _, filename, _, _ := runtime.Caller(0) + dir := filepath.Dir(filename) + url := r.URL.Path + path := dir + "/assets" + url + + if _, err := os.Stat(path); err == nil { + // Serve file from disk to make testing easy + http.ServeFile(w, r, path) + } else { + // Passthrough to the default asset handler if file not found on disk + next.ServeHTTP(w, r) + } + }) + }, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Screen Demo", + Width: 800, + Height: 600, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/screen/screens.go b/v3/examples/screen/screens.go new file mode 100644 index 000000000..a19afb14b --- /dev/null +++ b/v3/examples/screen/screens.go @@ -0,0 +1,137 @@ +package main + +import ( + "runtime" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type ScreenService struct { + screenManager application.ScreenManager + isExampleLayout bool +} + +func (s *ScreenService) GetSystemScreens() []*application.Screen { + s.isExampleLayout = false + screens := application.Get().Screen.GetAll() + return screens +} + +func (s *ScreenService) ProcessExampleScreens(rawScreens []interface{}) []*application.Screen { + s.isExampleLayout = true + + parseRect := func(m map[string]interface{}) application.Rect { + return application.Rect{ + X: int(m["X"].(float64)), + Y: int(m["Y"].(float64)), + Width: int(m["Width"].(float64)), + Height: int(m["Height"].(float64)), + } + } + + // Prevent unbounded slice growth by limiting the number of screens + maxScreens := 32 // Reasonable limit for screen configurations + if len(rawScreens) > maxScreens { + rawScreens = rawScreens[:maxScreens] + } + + screens := make([]*application.Screen, 0, len(rawScreens)) + for _, s := range rawScreens { + s := s.(map[string]interface{}) + + bounds := parseRect(s["Bounds"].(map[string]interface{})) + + screens = append(screens, &application.Screen{ + ID: s["ID"].(string), + Name: s["Name"].(string), + X: bounds.X, + Y: bounds.Y, + Size: application.Size{Width: bounds.Width, Height: bounds.Height}, + Bounds: bounds, + PhysicalBounds: parseRect(s["PhysicalBounds"].(map[string]interface{})), + WorkArea: parseRect(s["WorkArea"].(map[string]interface{})), + PhysicalWorkArea: parseRect(s["PhysicalWorkArea"].(map[string]interface{})), + IsPrimary: s["IsPrimary"].(bool), + ScaleFactor: float32(s["ScaleFactor"].(float64)), + Rotation: 0, + }) + } + + s.screenManager.LayoutScreens(screens) + return s.screenManager.GetAll() +} + +func (s *ScreenService) transformPoint(point application.Point, toDIP bool) application.Point { + if s.isExampleLayout { + if toDIP { + return s.screenManager.PhysicalToDipPoint(point) + } else { + return s.screenManager.DipToPhysicalPoint(point) + } + } else { + // ======================= + // TODO: remove this block when DPI is implemented in Linux & Mac + if runtime.GOOS != "windows" { + println("DPI not implemented yet!") + return point + } + // ======================= + if toDIP { + return application.PhysicalToDipPoint(point) + } else { + return application.DipToPhysicalPoint(point) + } + } +} + +func (s *ScreenService) TransformPoint(point map[string]interface{}, toDIP bool) (points [2]application.Point) { + pt := application.Point{ + X: int(point["X"].(float64)), + Y: int(point["Y"].(float64)), + } + + ptTransformed := s.transformPoint(pt, toDIP) + ptDblTransformed := s.transformPoint(ptTransformed, !toDIP) + + // double-transform a limited number of times to catch any double-rounding issues + // Limit iterations to prevent potential performance issues + maxIterations := 3 // Reduced from 10 to limit computational overhead + for i := 0; i < maxIterations; i++ { + ptTransformed = s.transformPoint(ptDblTransformed, toDIP) + ptDblTransformed = s.transformPoint(ptTransformed, !toDIP) + } + + points[0] = ptTransformed + points[1] = ptDblTransformed + return points +} + +func (s *ScreenService) TransformRect(rect map[string]interface{}, toDIP bool) application.Rect { + r := application.Rect{ + X: int(rect["X"].(float64)), + Y: int(rect["Y"].(float64)), + Width: int(rect["Width"].(float64)), + Height: int(rect["Height"].(float64)), + } + + if s.isExampleLayout { + if toDIP { + return s.screenManager.PhysicalToDipRect(r) + } else { + return s.screenManager.DipToPhysicalRect(r) + } + } else { + // ======================= + // TODO: remove this block when DPI is implemented in Linux & Mac + if runtime.GOOS != "windows" { + println("DPI not implemented yet!") + return r + } + // ======================= + if toDIP { + return application.PhysicalToDipRect(r) + } else { + return application.DipToPhysicalRect(r) + } + } +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/index.js new file mode 100644 index 000000000..cb6c1ff84 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Hashes} Hashes + */ diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/models.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/models.js new file mode 100644 index 000000000..a48737a6b --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/models.js @@ -0,0 +1,14 @@ +// @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 "/wails/runtime.js"; + +/** + * @typedef {Object} Hashes + * @property {string} md5 + * @property {string} sha1 + * @property {string} sha256 + */ diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/service.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/service.js new file mode 100644 index 000000000..f5c01b306 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/service.js @@ -0,0 +1,19 @@ +// @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 {Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create} from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {string} s + * @returns {$CancellablePromise<$models.Hashes>} + */ +export function Generate(s) { + return $Call.ByID(1123907498, s); +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js new file mode 100644 index 000000000..d2bf43c94 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/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 Service from "./service.js"; +export { + Service +}; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js new file mode 100644 index 000000000..e69de29bb diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js new file mode 100644 index 000000000..b4285097c --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js @@ -0,0 +1,69 @@ +// @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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +/** + * Clear deletes all keys from the store. If AutoSave is true, the store is saved to disk. + * @returns {Promise & { cancel(): void }} + */ +export function Clear() { + let $resultPromise = /** @type {any} */($Call.ByID(816318109)); + return $resultPromise; +} + +/** + * Delete deletes the given key from the store. If AutoSave is true, the store is saved to disk. + * @param {string} key + * @returns {Promise & { cancel(): void }} + */ +export function Delete(key) { + let $resultPromise = /** @type {any} */($Call.ByID(2889946731, key)); + return $resultPromise; +} + +/** + * Get returns the value for the given key. If key is empty, the entire store is returned. + * @param {string} key + * @returns {Promise & { cancel(): void }} + */ +export function Get(key) { + let $resultPromise = /** @type {any} */($Call.ByID(376909388, key)); + return $resultPromise; +} + +/** + * Load loads the store from disk. + * If the store is in-memory, i.e. not associated with a file, Load has no effect. + * If the operation fails, a non-nil error is returned + * and the store's content and state at call time are preserved. + * @returns {Promise & { cancel(): void }} + */ +export function Load() { + let $resultPromise = /** @type {any} */($Call.ByID(1850778156)); + return $resultPromise; +} + +/** + * Save saves the store to disk. + * If the store is in-memory, i.e. not associated with a file, Save has no effect. + * @returns {Promise & { cancel(): void }} + */ +export function Save() { + let $resultPromise = /** @type {any} */($Call.ByID(3572737965)); + return $resultPromise; +} + +/** + * Set sets the value for the given key. If AutoSave is true, the store is saved to disk. + * @param {string} key + * @param {any} value + * @returns {Promise & { cancel(): void }} + */ +export function Set(key, value) { + let $resultPromise = /** @type {any} */($Call.ByID(2491766752, key, value)); + return $resultPromise; +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js new file mode 100644 index 000000000..564a31eeb --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Level +} from "./models.js"; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/models.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/models.js new file mode 100644 index 000000000..d8579b51a --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/models.js @@ -0,0 +1,26 @@ +// @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 "/wails/runtime.js"; + +/** + * A Level is the importance or severity of a log event. + * The higher the level, the more important or severe the event. + * + * Values are arbitrary, but there are four predefined ones. + * @typedef {number} Level + */ + +/** + * Predefined constants for type Level. + * @namespace + */ +export const Level = { + Debug: -4, + Info: 0, + Warning: 4, + Error: 8, +}; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/service.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/service.js new file mode 100644 index 000000000..e5128d9bc --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/service.js @@ -0,0 +1,106 @@ +// @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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * DebugContext logs at level [Debug]. + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +function DebugContext(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(1481024990, message, args)); + return $resultPromise; +} + +/** + * ErrorContext logs at level [Error]. + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +function ErrorContext(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(4028761057, message, args)); + return $resultPromise; +} + +/** + * InfoContext logs at level [Info]. + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +function InfoContext(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(1400061359, message, args)); + return $resultPromise; +} + +/** + * Log emits a log record with the current time and the given level and message. + * The Record's attributes consist of the Logger's attributes followed by + * the attributes specified by args. + * + * The attribute arguments are processed as follows: + * - If an argument is a string and this is not the last argument, + * the following argument is treated as the value and the two are combined + * into an attribute. + * - Otherwise, the argument is treated as a value with key "!BADKEY". + * + * Log feeds the binding call context into the configured logger, + * so custom handlers may access context values, e.g. the current window. + * @param {$models.Level} level + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +export function Log(level, message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(4156699346, level, message, args)); + return $resultPromise; +} + +/** + * LogLevel returns the currently configured log level, + * that is either the one configured initially + * or the last value passed to [Service.SetLogLevel]. + * @returns {Promise<$models.Level> & { cancel(): void }} + */ +export function LogLevel() { + let $resultPromise = /** @type {any} */($Call.ByID(4058368160)); + return $resultPromise; +} + +/** + * SetLogLevel changes the current log level. + * @param {$models.Level} level + * @returns {Promise & { cancel(): void }} + */ +export function SetLogLevel(level) { + let $resultPromise = /** @type {any} */($Call.ByID(3988219088, level)); + return $resultPromise; +} + +/** + * WarningContext logs at level [Warn]. + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +function WarningContext(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(52282975, message, args)); + return $resultPromise; +} + +export { + DebugContext as Debug, + InfoContext as Info, + WarningContext as Warning, + ErrorContext as Error, +}; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js new file mode 100644 index 000000000..0918a51f0 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js @@ -0,0 +1,22 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +import * as $models from "./models.js"; + +/** + * Row holds a single row in the result of a query. + * It is a key-value map where keys are column names. + * @typedef {$models.Row} Row + */ + +/** + * Rows holds the result of a query + * as an array of key-value maps where keys are column names. + * @typedef {$models.Rows} Rows + */ diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/models.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/models.js new file mode 100644 index 000000000..041151c86 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/models.js @@ -0,0 +1,25 @@ +// @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 "/wails/runtime.js"; + +/** + * Row holds a single row in the result of a query. + * It is a key-value map where keys are column names. + * @typedef {{ [_: string]: any }} Row + */ + +/** + * Rows holds the result of a query + * as an array of key-value maps where keys are column names. + * @typedef {Row[]} Rows + */ + +/** + * Stmt wraps a prepared sql statement pointer. + * It provides the same methods as the [sql.Stmt] type. + * @typedef {string} Stmt + */ diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js new file mode 100644 index 000000000..9330c1e4d --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js @@ -0,0 +1,167 @@ +// @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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Close closes the current database connection if one is open, otherwise has no effect. + * Additionally, Close closes all open prepared statements associated to the connection. + * + * Even when a non-nil error is returned, + * the database service is left in a consistent state, + * ready for a call to [Service.Open]. + * @returns {Promise & { cancel(): void }} + */ +export function Close() { + let $resultPromise = /** @type {any} */($Call.ByID(1888105376)); + return $resultPromise; +} + +/** + * ClosePrepared closes a prepared statement + * obtained with [Service.Prepare] or [Service.PrepareContext]. + * ClosePrepared is idempotent: + * it has no effect on prepared statements that are already closed. + * @param {$models.Stmt | null} stmt + * @returns {Promise & { cancel(): void }} + */ +function ClosePrepared(stmt) { + let $resultPromise = /** @type {any} */($Call.ByID(2526200629, stmt)); + return $resultPromise; +} + +/** + * ExecContext executes a query without returning any rows. + * It supports early cancellation. + * @param {string} query + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +function ExecContext(query, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(674944556, query, args)); + return $resultPromise; +} + +/** + * ExecPrepared executes a prepared statement + * obtained with [Service.Prepare] or [Service.PrepareContext] + * without returning any rows. + * It supports early cancellation. + * @param {$models.Stmt | null} stmt + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +function ExecPrepared(stmt, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(2086877656, stmt, args)); + return $resultPromise; +} + +/** + * Open validates the current configuration, + * closes the current connection if one is present, + * then opens and validates a new connection. + * + * Even when a non-nil error is returned, + * the database service is left in a consistent state, + * ready for a new call to Open. + * @returns {Promise & { cancel(): void }} + */ +export function Open() { + let $resultPromise = /** @type {any} */($Call.ByID(2012175612)); + return $resultPromise; +} + +/** + * PrepareContext creates a prepared statement for later queries or executions. + * Multiple queries or executions may be run concurrently from the returned statement. + * + * The caller must call the statement's Close method when it is no longer needed. + * Statements are closed automatically + * when the connection they are associated with is closed. + * + * PrepareContext supports early cancellation. + * @param {string} query + * @returns {Promise<$models.Stmt | null> & { cancel(): void }} + */ +function PrepareContext(query) { + let $resultPromise = /** @type {any} */($Call.ByID(570941694, query)); + return $resultPromise; +} + +/** + * QueryContext executes a query and returns a slice of key-value records, + * one per row, with column names as keys. + * It supports early cancellation, returning the slice of results fetched so far. + * @param {string} query + * @param {any[]} args + * @returns {Promise<$models.Rows> & { cancel(): void }} + */ +function QueryContext(query, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(4115542347, query, args)); + let $typingPromise = /** @type {any} */($resultPromise.then(($result) => { + return $$createType1($result); + })); + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +/** + * QueryPrepared executes a prepared statement + * obtained with [Service.Prepare] or [Service.PrepareContext] + * and returns a slice of key-value records, one per row, with column names as keys. + * It supports early cancellation, returning the slice of results fetched so far. + * @param {$models.Stmt | null} stmt + * @param {any[]} args + * @returns {Promise<$models.Rows> & { cancel(): void }} + */ +function QueryPrepared(stmt, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(3885083725, stmt, args)); + let $typingPromise = /** @type {any} */($resultPromise.then(($result) => { + return $$createType1($result); + })); + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +// Private type creation functions +const $$createType0 = $Create.Map($Create.Any, $Create.Any); +const $$createType1 = $Create.Array($$createType0); + +export { + ExecContext as Execute, + QueryContext as Query +}; + +import { Stmt } from "./stmt.js"; + +/** + * Prepare creates a prepared statement for later queries or executions. + * Multiple queries or executions may be run concurrently from the returned statement. + * + * The caller must call the statement's Close method when it is no longer needed. + * Statements are closed automatically + * when the connection they are associated with is closed. + * + * Prepare supports early cancellation. + * + * @param {string} query + * @returns {Promise & { cancel(): void }} + */ +export function Prepare(query) { + const promise = PrepareContext(query); + const wrapper = /** @type {any} */(promise.then(function (id) { + return id == null ? null : new Stmt( + ClosePrepared.bind(null, id), + ExecPrepared.bind(null, id), + QueryPrepared.bind(null, id)); + })); + wrapper.cancel = promise.cancel; + return wrapper; +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/stmt.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/stmt.js new file mode 100644 index 000000000..948b0c3dd --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/stmt.js @@ -0,0 +1,79 @@ +//@ts-check + +//@ts-ignore: Unused imports +import * as $models from "./models.js"; + +const execSymbol = Symbol("exec"), + querySymbol = Symbol("query"), + closeSymbol = Symbol("close"); + +/** + * Stmt represents a prepared statement for later queries or executions. + * Multiple queries or executions may be run concurrently on the same statement. + * + * The caller must call the statement's Close method when it is no longer needed. + * Statements are closed automatically + * when the connection they are associated with is closed. + */ +export class Stmt { + /** + * Constructs a new prepared statement instance. + * @param {(...args: any[]) => Promise} close + * @param {(...args: any[]) => Promise & { cancel(): void }} exec + * @param {(...args: any[]) => Promise<$models.Rows> & { cancel(): void }} query + */ + constructor(close, exec, query) { + /** + * @member + * @private + * @type {typeof close} + */ + this[closeSymbol] = close; + + /** + * @member + * @private + * @type {typeof exec} + */ + this[execSymbol] = exec; + + /** + * @member + * @private + * @type {typeof query} + */ + this[querySymbol] = query; + } + + /** + * Closes the prepared statement. + * It has no effect when the statement is already closed. + * @returns {Promise} + */ + Close() { + return this[closeSymbol](); + } + + /** + * Executes the prepared statement without returning any rows. + * It supports early cancellation. + * + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ + Exec(...args) { + return this[execSymbol](...args); + } + + /** + * Executes the prepared statement + * and returns a slice of key-value records, one per row, with column names as keys. + * It supports early cancellation, returning the array of results fetched so far. + * + * @param {any[]} args + * @returns {Promise<$models.Rows> & { cancel(): void }} + */ + Query(...args) { + return this[querySymbol](...args); + } +} diff --git a/v3/examples/services/assets/index.html b/v3/examples/services/assets/index.html new file mode 100644 index 000000000..c44a0679a --- /dev/null +++ b/v3/examples/services/assets/index.html @@ -0,0 +1,287 @@ + + + + + Wails Alpha + + + + + + +

Services

+
+
+ + + + +
+
+

The sqlite service provides easy integration with sqlite dbs.

+

The demo DB has a single table: Users.

+

Enter a query below and hit the "Run" button.

+
+
+
+ +
+
+
+
+
+
+

The hashes service provides hashing functions.

+
+
+
+ +
+
+
+
+
+
+

The kvstore service provides a means for reading and writing to a json file.

+

Enter a key/value pair in the form below to add it to the file.

+

A blank value will remove the key.

+
+
+ + + +
+
+
+
+
+
+

The log plugin provides a means for sending frontend logs to a Go logger.

+

Enter some text below, press submit and check your console logs.

+
+ + + +
+
+
+ + diff --git a/v3/examples/services/assets/style.css b/v3/examples/services/assets/style.css new file mode 100644 index 000000000..f128a1aa1 --- /dev/null +++ b/v3/examples/services/assets/style.css @@ -0,0 +1,213 @@ +html { + background-color: rgba(33, 37, 43); + text-align: center; + color: white; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -webkit-touch-callout: none; + height: 100vh; + width: 100%; +} + +body { + padding-top: 40px; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + overscroll-behavior: none; + overflow-y: hidden; + background-image: url("/files/images/eryri1.png"); + background-color: rgba(33, 37, 43, 0.85); + background-blend-mode: overlay; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + height: 100vh; + width: 100%; +} + +.logo { + width: 30%; + height: 20%; +} +/* CSS */ +.button-42 { + background-color: initial; + background-image: linear-gradient(-180deg, #555555, #2f2f2f); + border-radius: 6px; + box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px; + color: #FFFFFF; + cursor: pointer; + display: inline-block; + font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif; + height: 35px; + line-height: 35px; + outline: 0; + overflow: hidden; + padding: 0 20px; + pointer-events: auto; + position: relative; + text-align: center; + touch-action: manipulation; + user-select: none; + -webkit-user-select: none; + vertical-align: top; + white-space: nowrap; + z-index: 9; + border: 0; + transition: box-shadow .2s; + font-size: medium; +} +p { + font-size: 1rem; +} + +.button-42:hover { + background-image: linear-gradient(-180deg, #cb3939, #610000); +} +html { + background-color: rgba(33, 37, 43); + text-align: center; + color: white; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -webkit-touch-callout: none; +} + +body { + padding-top: 40px; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + overscroll-behavior: none; +} + +.button-42 { + background-color: initial; + background-image: linear-gradient(-180deg, #555555, #2f2f2f); + border-radius: 6px; + box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px; + color: #FFFFFF; + cursor: pointer; + display: inline-block; + font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif; + height: 35px; + line-height: 35px; + outline: 0; + overflow: hidden; + padding: 0 20px; + pointer-events: auto; + position: relative; + text-align: center; + touch-action: manipulation; + user-select: none; + -webkit-user-select: none; + vertical-align: top; + white-space: nowrap; + z-index: 9; + border: 0; + transition: box-shadow .2s; + font-size: medium; +} + +p { + font-size: 1rem; +} + +.button-42:hover { + background-image: linear-gradient(-180deg, #cb3939, #610000); +} + +.tab { + overflow: hidden; + background-color: #f1f1f100; + margin-left: 20px; +} + +.tab button { + background-color: transparent; /* Make the background transparent */ + color: white; /* Make the text white */ + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 14px 16px; + transition: 0.3s; + position: relative; /* Added for the underline */ +} + +.tab button::after { /* Added for the underline */ + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: 0; + left: 0; + background-color: #a40505; + visibility: hidden; + transition: all 0.3s ease-in-out; +} + +.tab button:hover::after, /* Added for the underline */ +.tab button.active::after { /* Added for the underline */ + width: 100%; + visibility: visible; +} + +.tab button.active { + background-color: transparent; /* Make the background transparent */ + color: red; +} +.tabcontent { + display: none; + padding: 6px 12px; + border-top: none; +} +#sqlresults, #hashresults { + font-family: 'Courier New', Courier, monospace; + min-height: 100px; +} +#selectquery { + width: 90%; +} +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; +} + +th, td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; +} + +th { + background-color: #005467; + color: white; +} + +tr:hover { + background-color: #888; +} + +.error-message { + color: #FF4B2B; /* Bright red color for better visibility in dark mode */ + background-color: #1E1E1E; /* Dark background for the error message */ + border: 1px solid #FF4B2B; /* Border color same as text color */ + padding: 10px; /* Padding around the text */ + margin: 10px 0; /* Margin top and bottom */ + border-radius: 5px; /* Rounded corners */ + text-align: center; /* Center the text */ +} + +.narrowColumn { + width: 1%; /* Adjust as needed */ + white-space: nowrap; +} \ No newline at end of file diff --git a/v3/examples/services/files/images/eryri1.png b/v3/examples/services/files/images/eryri1.png new file mode 100644 index 000000000..224d3b4ac Binary files /dev/null and b/v3/examples/services/files/images/eryri1.png differ diff --git a/v3/examples/services/hashes/hashes.go b/v3/examples/services/hashes/hashes.go new file mode 100644 index 000000000..1e4653dbd --- /dev/null +++ b/v3/examples/services/hashes/hashes.go @@ -0,0 +1,45 @@ +package hashes + +import ( + "context" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Hashes = struct { + MD5 string `json:"md5"` + SHA1 string `json:"sha1"` + SHA256 string `json:"sha256"` +} + +type Service struct{} + +func (h *Service) Generate(s string) Hashes { + md5Hash := md5.Sum([]byte(s)) + sha1Hash := sha1.Sum([]byte(s)) + sha256Hash := sha256.Sum256([]byte(s)) + + return Hashes{ + MD5: hex.EncodeToString(md5Hash[:]), + SHA1: hex.EncodeToString(sha1Hash[:]), + SHA256: hex.EncodeToString(sha256Hash[:]), + } +} + +func New() *Service { + return &Service{} +} + +func (h *Service) ServiceName() string { + return "Hashes Service" +} + +func (h *Service) ServiceStartup(context.Context, application.ServiceOptions) error { + return nil +} + +func (h *Service) ServiceShutdown() error { return nil } diff --git a/v3/examples/services/main.go b/v3/examples/services/main.go new file mode 100644 index 000000000..c753a79c7 --- /dev/null +++ b/v3/examples/services/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "embed" + "log/slog" + "os" + "path/filepath" + "runtime" + + "github.com/wailsapp/wails/v3/examples/services/hashes" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/services/fileserver" + "github.com/wailsapp/wails/v3/pkg/services/kvstore" + "github.com/wailsapp/wails/v3/pkg/services/log" + "github.com/wailsapp/wails/v3/pkg/services/sqlite" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + // Get the local directory of this source file + // This isn't needed when running the example with `go run .` + // but is needed when running the example from an IDE + _, thisFile, _, _ := runtime.Caller(0) + localDir := filepath.Dir(thisFile) + + rootPath := filepath.Join(localDir, "files") + app := application.New(application.Options{ + Name: "Services Demo", + Description: "A demo of the services API", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + LogLevel: slog.LevelDebug, + Services: []application.Service{ + application.NewService(hashes.New()), + application.NewService(sqlite.NewWithConfig(&sqlite.Config{ + DBSource: "test.db", + })), + application.NewService(kvstore.NewWithConfig(&kvstore.Config{ + Filename: "store.json", + AutoSave: true, + })), + application.NewService(log.New()), + application.NewServiceWithOptions(fileserver.NewWithConfig(&fileserver.Config{ + RootPath: rootPath, + }), application.ServiceOptions{ + Route: "/files", + }), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 1024, + Height: 768, + }) + + err := app.Run() + + if err != nil { + println(err.Error()) + os.Exit(1) + } +} diff --git a/v3/examples/services/store.json b/v3/examples/services/store.json new file mode 100644 index 000000000..a56bb9842 --- /dev/null +++ b/v3/examples/services/store.json @@ -0,0 +1 @@ +{"q":"www","url2":"https://reddit.com"} \ No newline at end of file diff --git a/v3/examples/services/test.db b/v3/examples/services/test.db new file mode 100644 index 000000000..ced6a916c Binary files /dev/null and b/v3/examples/services/test.db differ diff --git a/v3/examples/show-macos-toolbar/README.md b/v3/examples/show-macos-toolbar/README.md new file mode 100644 index 000000000..21bbeac96 --- /dev/null +++ b/v3/examples/show-macos-toolbar/README.md @@ -0,0 +1,20 @@ +# Show macOS Toolbar Example + +This example is a demonstration of the macOS option `ShowToolbarWhenFullscreen`, which keeps +the system toolbar visible when in fullscreen mode. + +## Running the example + +To run the example (on macOS), simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | N/A | +| Linux | N/A | diff --git a/v3/examples/show-macos-toolbar/main.go b/v3/examples/show-macos-toolbar/main.go new file mode 100644 index 000000000..648432da6 --- /dev/null +++ b/v3/examples/show-macos-toolbar/main.go @@ -0,0 +1,51 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Show macOS Toolbar", + Description: "A demo of the ShowToolbarWhenFullscreen option", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create window + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Toolbar hidden (default behaviour)", + HTML: "

Switch this window to fullscreen: the toolbar will be hidden

", + CSS: `body { background-color: blue; color: white; height: 100vh; display: flex; justify-content: center; align-items: center; }`, + Mac: application.MacWindow{ + TitleBar: application.MacTitleBar{ + UseToolbar: true, + HideToolbarSeparator: true, + }, + }, + }) + + // Create window + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Toolbar visible", + HTML: "

Switch this window to fullscreen: the toolbar will stay visible

", + CSS: `body { background-color: red; color: white; height: 100vh; display: flex; justify-content: center; align-items: center; }`, + Mac: application.MacWindow{ + TitleBar: application.MacTitleBar{ + UseToolbar: true, + HideToolbarSeparator: true, + ShowToolbarWhenFullscreen: true, + }, + }, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/single-instance/README.md b/v3/examples/single-instance/README.md new file mode 100644 index 000000000..5982cb909 --- /dev/null +++ b/v3/examples/single-instance/README.md @@ -0,0 +1,62 @@ +# Single Instance Example + +This example demonstrates the single instance functionality in Wails v3. It shows how to: + +1. Ensure only one instance of your application can run at a time +2. Notify the first instance when a second instance is launched +3. Pass data between instances +4. Handle command line arguments and working directory information from second instances + +## Running the Example + +1. Build and run the application: + ```bash + go build + ./single-instance + ``` + +2. Try launching a second instance of the application. You'll notice: + - The second instance will exit immediately + - The first instance will receive and display: + - Command line arguments from the second instance + - Working directory of the second instance + - Additional data passed from the second instance + +3. Check the application logs to see the information received from second instances. + +## Features Demonstrated + +- Setting up single instance lock with a unique identifier +- Handling second instance launches through callbacks +- Passing custom data between instances +- Displaying instance information in a web UI +- Cross-platform support (Windows, macOS, Linux) + +## Code Overview + +The example consists of: + +- `main.go`: The main application code demonstrating single instance setup +- A simple web UI showing current instance information +- Callback handling for second instance launches + +## Implementation Details + +The application uses the Wails v3 single instance feature: + +```go +app := application.New(&application.Options{ + SingleInstance: &application.SingleInstanceOptions{ + UniqueID: "com.wails.example.single-instance", + OnSecondInstance: func(data application.SecondInstanceData) { + // Handle second instance launch + }, + AdditionalData: map[string]string{ + }, + }, +}) +``` + +The implementation uses platform-specific mechanisms: +- Windows: Named mutex and window messages +- Unix (Linux/macOS): File locking with flock and signals diff --git a/v3/examples/single-instance/assets/index.html b/v3/examples/single-instance/assets/index.html new file mode 100644 index 000000000..330b062ec --- /dev/null +++ b/v3/examples/single-instance/assets/index.html @@ -0,0 +1,141 @@ + + + + + + Single Instance Demo + + + + +
+

Single Instance Demo

+ +
+

Current Instance Information

+
Loading...
+
+ +
+

Instructions

+

Try launching another instance of this application. The first instance will:

+
    +
  • Receive notification of the second instance launch
  • +
  • Get the command line arguments of the second instance
  • +
  • Get the working directory of the second instance
  • +
  • Receive any additional data passed from the second instance
  • +
+

Check the application logs to see the information received from second instances.

+
+
+ + + + \ No newline at end of file diff --git a/v3/examples/single-instance/main.go b/v3/examples/single-instance/main.go new file mode 100644 index 000000000..51ca48a38 --- /dev/null +++ b/v3/examples/single-instance/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "embed" + "log" + "log/slog" + "os" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/index.html +var assets embed.FS + +type App struct{} + +func (a *App) GetCurrentInstanceInfo() map[string]interface{} { + return map[string]interface{}{ + "args": os.Args, + "workingDir": getCurrentWorkingDir(), + } +} + +var encryptionKey = [32]byte{ + 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, + 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, + 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, + 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, +} + +func main() { + + var window *application.WebviewWindow + app := application.New(application.Options{ + Name: "Single Instance Example", + LogLevel: slog.LevelDebug, + Description: "An example of single instance functionality in Wails v3", + Services: []application.Service{ + application.NewService(&App{}), + }, + SingleInstance: &application.SingleInstanceOptions{ + UniqueID: "com.wails.example.single-instance", + EncryptionKey: encryptionKey, + OnSecondInstanceLaunch: func(data application.SecondInstanceData) { + if window != nil { + window.EmitEvent("secondInstanceLaunched", data) + window.Restore() + window.Focus() + } + log.Printf("Second instance launched with args: %v\n", data.Args) + log.Printf("Working directory: %s\n", data.WorkingDir) + if data.AdditionalData != nil { + log.Printf("Additional data: %v\n", data.AdditionalData) + } + }, + AdditionalData: map[string]string{ + "launchtime": time.Now().Local().String(), + }, + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + window = app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Single Instance Demo", + Width: 800, + Height: 700, + URL: "/", + }) + + app.Run() +} + +func getCurrentWorkingDir() string { + dir, err := os.Getwd() + if err != nil { + return "" + } + return dir +} diff --git a/v3/examples/systray-basic/README.md b/v3/examples/systray-basic/README.md new file mode 100644 index 000000000..11b84a0bf --- /dev/null +++ b/v3/examples/systray-basic/README.md @@ -0,0 +1,16 @@ +# Systray Basic Example + +This example creates a simple system tray with an attached window. +The window is hidden by default and toggled by left-clicking on the systray icon. +The window will hide automatically when it loses focus. + +On Windows, if the icon is in the notification flyout, +then the window will be shown in the bottom right corner. + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/systray-basic/main.go b/v3/examples/systray-basic/main.go new file mode 100644 index 000000000..026758a02 --- /dev/null +++ b/v3/examples/systray-basic/main.go @@ -0,0 +1,61 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/events" + "log" + "runtime" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/icons" +) + +func main() { + app := application.New(application.Options{ + Name: "Systray Demo", + Description: "A demo of the Systray API", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ActivationPolicy: application.ActivationPolicyAccessory, + }, + }) + + systemTray := app.SystemTray.New() + + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 500, + Height: 500, + Name: "Systray Demo Window", + Frameless: true, + AlwaysOnTop: true, + Hidden: true, + DisableResize: true, + Windows: application.WindowsWindow{ + HiddenOnTaskbar: true, + }, + KeyBindings: map[string]func(window application.Window){ + "F12": func(window application.Window) { + systemTray.OpenMenu() + }, + }, + }) + + // Register a hook to hide the window when the window is closing + window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + // Hide the window + window.Hide() + // Cancel the event so it doesn't get destroyed + e.Cancel() + }) + + if runtime.GOOS == "darwin" { + systemTray.SetTemplateIcon(icons.SystrayMacTemplate) + } + + systemTray.AttachWindow(window).WindowOffset(5) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/systray-custom/README.md b/v3/examples/systray-custom/README.md new file mode 100644 index 000000000..598d84496 --- /dev/null +++ b/v3/examples/systray-custom/README.md @@ -0,0 +1,16 @@ +# Systray Custom Example + +This example creates a simple system tray and uses hooks to attach a custom window. +The window is hidden by default and toggled by left-clicking on the systray icon. +The window will hide automatically when it loses focus. + +On Windows, if the icon is in the notification flyout, +then the window will be shown in the bottom right corner. + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | | +| Linux | | diff --git a/v3/examples/systray-custom/main.go b/v3/examples/systray-custom/main.go new file mode 100644 index 000000000..f10140792 --- /dev/null +++ b/v3/examples/systray-custom/main.go @@ -0,0 +1,73 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/icons" + "log" + "runtime" +) + +var windowShowing bool + +func createWindow(app *application.App) { + if windowShowing { + return + } + // Log the time taken to create the window + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 500, + Height: 500, + Name: "Systray Demo Window", + AlwaysOnTop: true, + Hidden: true, + BackgroundColour: application.NewRGB(33, 37, 41), + DisableResize: true, + Windows: application.WindowsWindow{ + HiddenOnTaskbar: true, + }, + }) + windowShowing = true + + window.OnWindowEvent(events.Common.WindowClosing, func(e *application.WindowEvent) { + windowShowing = false + }) + + window.Show() +} + +func main() { + app := application.New(application.Options{ + Name: "Systray Demo", + Description: "A demo of the Systray API", + Assets: application.AlphaAssets, + Windows: application.WindowsOptions{ + DisableQuitOnLastWindowClosed: true, + }, + Mac: application.MacOptions{ + ActivationPolicy: application.ActivationPolicyAccessory, + }, + }) + + systemTray := app.SystemTray.New() + menu := app.NewMenu() + menu.Add("Quit").OnClick(func(data *application.Context) { + app.Quit() + }) + systemTray.SetMenu(menu) + systemTray.SetTooltip("Systray Demo") + + if runtime.GOOS == "darwin" { + systemTray.SetTemplateIcon(icons.SystrayMacTemplate) + } + + systemTray.OnClick(func() { + createWindow(app) + }) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/systray-menu/README.md b/v3/examples/systray-menu/README.md new file mode 100644 index 000000000..2de57faea --- /dev/null +++ b/v3/examples/systray-menu/README.md @@ -0,0 +1,17 @@ +# Systray Menu Example + +This example creates a system tray with an attached window and a menu. +The window is hidden by default and toggled by left-clicking on the systray icon. +The window will hide automatically when it loses focus. +Right-clicking on the systray icon will show the menu. + +On Windows, if the icon is in the notification flyout, +then the window will be shown in the bottom right corner. + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/systray-menu/logo-dark-xsmall.png b/v3/examples/systray-menu/logo-dark-xsmall.png new file mode 100644 index 000000000..83a514c74 Binary files /dev/null and b/v3/examples/systray-menu/logo-dark-xsmall.png differ diff --git a/v3/examples/systray-menu/main.go b/v3/examples/systray-menu/main.go new file mode 100644 index 000000000..d7ef2c6f4 --- /dev/null +++ b/v3/examples/systray-menu/main.go @@ -0,0 +1,114 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/events" + "log" + "runtime" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/icons" +) + +//go:embed logo-dark-xsmall.png +var logo []byte + +func main() { + app := application.New(application.Options{ + Name: "Systray Demo", + Description: "A demo of the Systray API", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ActivationPolicy: application.ActivationPolicyAccessory, + }, + }) + + systemTray := app.SystemTray.New() + + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Width: 500, + Height: 500, + Name: "Systray Demo Window", + Frameless: true, + AlwaysOnTop: true, + Hidden: true, + DisableResize: true, + Windows: application.WindowsWindow{ + HiddenOnTaskbar: true, + }, + KeyBindings: map[string]func(window application.Window){ + "F12": func(window application.Window) { + systemTray.OpenMenu() + }, + }, + }) + + window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + window.Hide() + e.Cancel() + }) + + if runtime.GOOS == "darwin" { + systemTray.SetTemplateIcon(icons.SystrayMacTemplate) + } + + myMenu := app.NewMenu() + myMenu.Add("Wails").SetBitmap(logo).SetEnabled(false) + myMenu.Add("Hidden").SetHidden(true) + + myMenu.Add("Hello World!").OnClick(func(ctx *application.Context) { + println("Hello World!") + q := application.QuestionDialog().SetTitle("Ready?").SetMessage("Are you feeling ready?") + q.AddButton("Yes").OnClick(func() { + println("Awesome!") + }) + q.AddButton("No").SetAsDefault().OnClick(func() { + println("Boo!") + }) + q.Show() + }) + subMenu := myMenu.AddSubmenu("Submenu") + subMenu.Add("Click me!").OnClick(func(ctx *application.Context) { + ctx.ClickedMenuItem().SetLabel("Clicked!") + }) + myMenu.AddSeparator() + myMenu.AddCheckbox("Checked", true).OnClick(func(ctx *application.Context) { + println("Checked: ", ctx.ClickedMenuItem().Checked()) + application.InfoDialog().SetTitle("Hello World!").SetMessage("Hello World!").Show() + }) + myMenu.Add("Enabled").OnClick(func(ctx *application.Context) { + println("Click me!") + ctx.ClickedMenuItem().SetLabel("Disabled!").SetEnabled(false) + }) + myMenu.AddSeparator() + // Callbacks can be shared. This is useful for radio groups + radioCallback := func(ctx *application.Context) { + menuItem := ctx.ClickedMenuItem() + menuItem.SetLabel(menuItem.Label() + "!") + } + + // Radio groups are created implicitly by placing radio items next to each other in a menu + myMenu.AddRadio("Radio 1", true).OnClick(radioCallback) + myMenu.AddRadio("Radio 2", false).OnClick(radioCallback) + myMenu.AddRadio("Radio 3", false).OnClick(radioCallback) + myMenu.AddSeparator() + myMenu.Add("Hide System tray for 3 seconds...").OnClick(func(ctx *application.Context) { + systemTray.Hide() + time.Sleep(3 * time.Second) + systemTray.Show() + }) + myMenu.AddSeparator() + myMenu.Add("Quit").OnClick(func(ctx *application.Context) { + app.Quit() + }) + + systemTray.SetMenu(myMenu) + + systemTray.AttachWindow(window).WindowOffset(2) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/video/README.md b/v3/examples/video/README.md new file mode 100644 index 000000000..012469afb --- /dev/null +++ b/v3/examples/video/README.md @@ -0,0 +1,19 @@ +# Video Example + +This example shows support for HTML5 video. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | | +| Windows | Working | +| Linux | | diff --git a/v3/examples/video/main.go b/v3/examples/video/main.go new file mode 100644 index 000000000..879172c61 --- /dev/null +++ b/v3/examples/video/main.go @@ -0,0 +1,47 @@ +package main + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/pkg/events" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Video Demo", + Description: "A demo of HTML5 Video API", + Assets: application.AlphaAssets, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Windows: application.WindowsOptions{ + WndProcInterceptor: nil, + DisableQuitOnLastWindowClosed: false, + WebviewUserDataPath: "", + WebviewBrowserPath: "", + }, + }) + app.Event.OnApplicationEvent(events.Mac.ApplicationDidFinishLaunching, func(event *application.ApplicationEvent) { + log.Println("ApplicationDidFinishLaunching") + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + BackgroundColour: application.NewRGB(33, 37, 41), + Mac: application.MacWindow{ + DisableShadow: true, + WebviewPreferences: application.MacWebviewPreferences{ + FullscreenEnabled: application.Enabled, + }, + }, + HTML: "", + }) + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/window-api/README.md b/v3/examples/window-api/README.md new file mode 100644 index 000000000..02c726062 --- /dev/null +++ b/v3/examples/window-api/README.md @@ -0,0 +1,19 @@ +# Window API Example + +This is an example of how to use the JS Window API + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | | +| Linux | | diff --git a/v3/examples/window-api/assets/index.html b/v3/examples/window-api/assets/index.html new file mode 100644 index 000000000..a0dd5d8b2 --- /dev/null +++ b/v3/examples/window-api/assets/index.html @@ -0,0 +1,159 @@ + + + + + Wails Alpha + + + + + +
Alpha
+
+

Close the Window?

+

Center

+

Minimise

+

Maximise

+

UnMaximise

+

Fullscreen

+

UnFullscreen

+

Restore

+
+
+

ToggleMaximise

+

IsFocused

+

IsMaximised

+

IsFullscreen

+
+
+ + + + + diff --git a/v3/examples/window-api/main.go b/v3/examples/window-api/main.go new file mode 100644 index 000000000..252608641 --- /dev/null +++ b/v3/examples/window-api/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "JS Window API Demo", + Description: "A demo of the JS Window API", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "JS Window API Demo", + Width: 1280, + Height: 1024, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/examples/window-call/README.md b/v3/examples/window-call/README.md new file mode 100644 index 000000000..baffad046 --- /dev/null +++ b/v3/examples/window-call/README.md @@ -0,0 +1,11 @@ +# Window Call Example + +This example is a demonstration of how to know which window is calling a service. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` diff --git a/v3/examples/window-call/assets/index.html b/v3/examples/window-call/assets/index.html new file mode 100644 index 000000000..a99293f03 --- /dev/null +++ b/v3/examples/window-call/assets/index.html @@ -0,0 +1,27 @@ + + + + + + Window Call Demo + + + + + + + + + \ No newline at end of file diff --git a/v3/examples/window-call/main.go b/v3/examples/window-call/main.go new file mode 100644 index 000000000..e6bdee23b --- /dev/null +++ b/v3/examples/window-call/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "context" + "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "log" + "math/rand" + "runtime" + "strconv" +) + +//go:embed assets/* +var assets embed.FS + +type WindowService struct{} + +func (s *WindowService) RandomTitle(ctx context.Context) { + callingWindow := ctx.Value(application.WindowKey).(application.Window) + title := "Random Title " + strconv.Itoa(rand.Intn(1000)) + callingWindow.SetTitle(title) +} + +// ============================================== + +func main() { + app := application.New(application.Options{ + Name: "Window call Demo", + Description: "A demo of the WebviewWindow API", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: false, + }, + Services: []application.Service{ + application.NewService(&WindowService{}), + }, + }) + + app.Window.New(). + SetTitle("WebviewWindow 1"). + Show() + + // Create a custom menu + menu := app.NewMenu() + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } + + windowCounter := 1 + + // Let's make a "Demo" menu + myMenu := menu.AddSubmenu("New") + + myMenu.Add("New WebviewWindow"). + SetAccelerator("CmdOrCtrl+N"). + OnClick(func(ctx *application.Context) { + app.Window.New(). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + Show() + windowCounter++ + }) + + app.Menu.Set(menu) + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/window-menu/README.md b/v3/examples/window-menu/README.md new file mode 100644 index 000000000..0d825364f --- /dev/null +++ b/v3/examples/window-menu/README.md @@ -0,0 +1,24 @@ +# Window Menu Example + +*** Windows Only *** + +This example demonstrates how to create a window with a menu bar that can be toggled using the window.ToggleMenuBar() method. + +## Features + +- Default menu bar with File, Edit, and Help menus +- F1 key to toggle menu bar visibility +- Simple HTML interface with instructions + +## Running the Example + +```bash +cd v3/examples/window-menu +go run . +``` + +## How it Works + +The example creates a window with a default menu and binds the F1 key to toggle the menu bar's visibility. The menu bar will show when F2 is pressed and hide when F3 is released. + +Note: The menu bar toggling functionality only works on Windows. On other platforms, the F1 key binding will have no effect. diff --git a/v3/examples/window-menu/assets/about.html b/v3/examples/window-menu/assets/about.html new file mode 100644 index 000000000..e887a84ce --- /dev/null +++ b/v3/examples/window-menu/assets/about.html @@ -0,0 +1,14 @@ + + + Window Menu Demo + + + +
+

About Window Menu Demo

+

Press F1 to toggle menu bar visibility

+

Press F2 to show menu bar

+

Press F3 to hide menu bar

+
+ + \ No newline at end of file diff --git a/v3/examples/window-menu/assets/index.html b/v3/examples/window-menu/assets/index.html new file mode 100644 index 000000000..b18f601e0 --- /dev/null +++ b/v3/examples/window-menu/assets/index.html @@ -0,0 +1,48 @@ + + + Window Menu Demo + + + +
+

Window Menu Demo

+

This example demonstrates the menu bar visibility toggle feature.

+

Press F1 to toggle the menu bar.

+

Press F2 to show the menu bar.

+

Press F3 to hide the menu bar.

+

The menu includes:

+
    +
  • File menu with Exit option
  • +
  • MenuBar menu with Hide options
  • +
  • Help menu with About
  • +
+
+ + \ No newline at end of file diff --git a/v3/examples/window-menu/assets/style.css b/v3/examples/window-menu/assets/style.css new file mode 100644 index 000000000..c7fc71f39 --- /dev/null +++ b/v3/examples/window-menu/assets/style.css @@ -0,0 +1,26 @@ +body { + font-family: system-ui, -apple-system, sans-serif; + margin: 0; + padding: 2rem; + background: #f5f5f5; + color: #333; +} +.container { + max-width: 600px; + margin: 0 auto; + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} +h1 { + margin-top: 0; + color: #2d2d2d; +} +.key { + background: #e9e9e9; + padding: 2px 8px; + border-radius: 4px; + border: 1px solid #ccc; + font-family: monospace; +} diff --git a/v3/examples/window-menu/main.go b/v3/examples/window-menu/main.go new file mode 100644 index 000000000..107213b44 --- /dev/null +++ b/v3/examples/window-menu/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "embed" + _ "embed" + "github.com/wailsapp/wails/v3/pkg/application" + "log" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + app := application.New(application.Options{ + Name: "Window MenuBar Demo", + Description: "A demo of menu bar toggling", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + // Create a menu + menu := app.NewMenu() + fileMenu := menu.AddSubmenu("File") + fileMenu.Add("Exit").OnClick(func(ctx *application.Context) { + app.Quit() + }) + + editMenu := menu.AddSubmenu("MenuBar") + editMenu.Add("Hide MenuBar").OnClick(func(ctx *application.Context) { + app.Window.Current().HideMenuBar() + }) + + helpMenu := menu.AddSubmenu("Help") + helpMenu.Add("About").OnClick(func(ctx *application.Context) { + app.Window.Current().SetURL("/about.html") + }) + + // Create window with menu + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window MenuBar Demo", + Width: 800, + Height: 600, + Windows: application.WindowsWindow{ + Menu: menu, + }, + KeyBindings: map[string]func(window application.Window){ + "F1": func(window application.Window) { + window.ToggleMenuBar() + }, + "F2": func(window application.Window) { + window.ShowMenuBar() + }, + "F3": func(window application.Window) { + window.HideMenuBar() + }, + }, + }) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/examples/window/README.md b/v3/examples/window/README.md new file mode 100644 index 000000000..2f1c7e810 --- /dev/null +++ b/v3/examples/window/README.md @@ -0,0 +1,19 @@ +# Window Example + +This example is a demonstration of the Windows API. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | | +| Windows | Working | +| Linux | | diff --git a/v3/examples/window/assets/index.html b/v3/examples/window/assets/index.html new file mode 100644 index 000000000..16baa68ff --- /dev/null +++ b/v3/examples/window/assets/index.html @@ -0,0 +1,90 @@ + + + + + + Window Demo + + + + + +
+
+ +
+
+
+ +
+ +  X: + + +  Width: + + +   + + +
+ + + +   + + + +   + +
+ + + + + + \ No newline at end of file diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go new file mode 100644 index 000000000..46a271c49 --- /dev/null +++ b/v3/examples/window/main.go @@ -0,0 +1,761 @@ +package main + +import ( + "embed" + "fmt" + "log" + "math/rand" + "runtime" + "strconv" + "time" + + "github.com/samber/lo" + "github.com/wailsapp/wails/v3/pkg/events" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// This is a stub for non-windows platforms +var getExStyle = func() int { + return 0 +} + +//go:embed assets/* +var assets embed.FS + +type WindowService struct{} + +// ============================================== +func (s *WindowService) SetPos(relative bool, x, y float64) { + win := application.Get().Window.Current() + initX, initY := win.Position() + if relative { + x += float64(initX) + y += float64(initY) + } + win.SetPosition(int(x), int(y)) + currentX, currentY := win.Position() + fmt.Printf("SetPos: %d, %d => %d, %d\n", initX, initY, currentX, currentY) +} +func (s *WindowService) SetSize(relative bool, wdt, hgt float64) { + win := application.Get().Window.Current() + initW, initH := win.Size() + if relative { + wdt += float64(initW) + hgt += float64(initH) + } + win.SetSize(int(wdt), int(hgt)) + currentW, currentH := win.Size() + fmt.Printf("SetSize: %d, %d => %d, %d\n", initW, initH, currentW, currentH) +} +func (s *WindowService) SetBounds(x, y, w, h float64) { + win := application.Get().Window.Current() + initR := win.Bounds() + win.SetBounds(application.Rect{ + X: int(x), + Y: int(y), + Width: int(w), + Height: int(h), + }) + currentR := win.Bounds() + fmt.Printf("SetBounds: %+v => %+v\n", initR, currentR) +} +func (s *WindowService) GetBounds() application.Rect { + win := application.Get().Window.Current() + r := win.Bounds() + mid := r.X + (r.Width-1)/2 + fmt.Printf("GetBounds: %+v: mid: %d\n", r, mid) + return r +} + +// ============================================== + +func main() { + app := application.New(application.Options{ + Name: "WebviewWindow Demo", + Description: "A demo of the WebviewWindow API", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: false, + }, + Services: []application.Service{ + application.NewService(&WindowService{}), + }, + }) + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) { + log.Println("ApplicationDidFinishLaunching") + }) + + var hiddenWindows []application.Window + + currentWindow := func(fn func(window application.Window)) { + if app.Window.Current() != nil { + fn(app.Window.Current()) + } else { + println("Current Window is nil") + } + } + + // Create a custom menu + menu := app.NewMenu() + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } else { + menu.AddRole(application.FileMenu) + } + windowCounter := 1 + + // Let's make a "Demo" menu + myMenu := menu.AddSubmenu("New") + + myMenu.Add("New WebviewWindow"). + SetAccelerator("CmdOrCtrl+N"). + OnClick(func(ctx *application.Context) { + app.Window.New(). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + if runtime.GOOS != "linux" { + myMenu.Add("New WebviewWindow (Content Protection Enabled)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MinimiseButtonState: application.ButtonDisabled, + ContentProtectionEnabled: true, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Disable Minimise)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MinimiseButtonState: application.ButtonDisabled, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Disable Maximise)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MaximiseButtonState: application.ButtonDisabled, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Hide Minimise)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MinimiseButtonState: application.ButtonHidden, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Always on top)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + AlwaysOnTop: true, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Hide Maximise)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MaximiseButtonState: application.ButtonHidden, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + + myMenu.Add("New WebviewWindow (Centered)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MaximiseButtonState: application.ButtonHidden, + InitialPosition: application.WindowCentered, + }). + SetTitle("WebviewWindow " + strconv.Itoa(windowCounter)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + + myMenu.Add("New WebviewWindow (Position 100,100)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + MaximiseButtonState: application.ButtonHidden, + X: 100, + Y: 100, + InitialPosition: application.WindowXY, + }). + SetTitle("WebviewWindow " + strconv.Itoa(windowCounter)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + } + if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { + myMenu.Add("New WebviewWindow (Disable Close)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + CloseButtonState: application.ButtonDisabled, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Hide Close)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + CloseButtonState: application.ButtonHidden, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + + } + + if runtime.GOOS == "windows" { + myMenu.Add("New WebviewWindow (Custom ExStyle)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Windows: application.WindowsWindow{ + ExStyle: getExStyle(), + }, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + } + myMenu.Add("New WebviewWindow (Listen to Move)"). + OnClick(func(ctx *application.Context) { + w := app.Window.NewWithOptions(application.WebviewWindowOptions{}). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + w.OnWindowEvent(events.Common.WindowDidMove, func(event *application.WindowEvent) { + x, y := w.Position() + fmt.Printf("WindowDidMove event triggered. New position: (%d, %d)\n", x, y) + }) + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Listen to Resize)"). + OnClick(func(ctx *application.Context) { + w := app.Window.NewWithOptions(application.WebviewWindowOptions{}). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + w.OnWindowEvent(events.Common.WindowDidResize, func(event *application.WindowEvent) { + width, height := w.Size() + + fmt.Printf("WindowDidResize event triggered. New size: (%d, %d)\n", width, height) + }) + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Hides on Close one time)"). + SetAccelerator("CmdOrCtrl+H"). + OnClick(func(ctx *application.Context) { + var w application.Window = app.Window.New() + + w.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { + if !lo.Contains(hiddenWindows, w) { + hiddenWindows = append(hiddenWindows, w) + go func() { + time.Sleep(5 * time.Second) + w.Show() + }() + w.Hide() + e.Cancel() + } + // Remove the window from the hiddenWindows list + hiddenWindows = lo.Without(hiddenWindows, w) + }) + + w.SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + + windowCounter++ + + }) + myMenu.Add("New WebviewWindow (Frameless)"). + SetAccelerator("CmdOrCtrl+F"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + X: rand.Intn(1000), + Y: rand.Intn(800), + BackgroundColour: application.NewRGB(33, 37, 41), + Frameless: true, + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + }, + }).Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Ignores mouse events)"). + SetAccelerator("CmdOrCtrl+F"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + HTML: "
", + X: rand.Intn(1000), + Y: rand.Intn(800), + IgnoreMouseEvents: true, + BackgroundType: application.BackgroundTypeTransparent, + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + }, + }).Show() + windowCounter++ + }) + if runtime.GOOS == "darwin" { + myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHiddenInset, + InvisibleTitleBarHeight: 25, + }, + }). + SetBackgroundColour(application.NewRGB(33, 37, 41)). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetHTML("

A MacTitleBarHiddenInset WebviewWindow example

"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetHTML("

A MacTitleBarHiddenInsetUnified WebviewWindow example

"). + Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (MacTitleBarHidden)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHidden, + InvisibleTitleBarHeight: 25, + }, + }). + SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetHTML("

A MacTitleBarHidden WebviewWindow example

"). + Show() + windowCounter++ + }) + } + if runtime.GOOS == "windows" { + myMenu.Add("New WebviewWindow (Mica)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "WebviewWindow " + strconv.Itoa(windowCounter), + X: rand.Intn(1000), + Y: rand.Intn(800), + BackgroundType: application.BackgroundTypeTranslucent, + HTML: ` + + + + + +
+

This is a Window with a Mica backdrop

+
+ +`, + Windows: application.WindowsWindow{ + BackdropType: application.Mica, + }, + }).Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Acrylic)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "WebviewWindow " + strconv.Itoa(windowCounter), + X: rand.Intn(1000), + Y: rand.Intn(800), + BackgroundType: application.BackgroundTypeTranslucent, + HTML: ` + + + + + +
+

This is a Window with an Acrylic backdrop

+
+ +`, + Windows: application.WindowsWindow{ + BackdropType: application.Acrylic, + }, + }).Show() + windowCounter++ + }) + myMenu.Add("New WebviewWindow (Tabbed)"). + OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "WebviewWindow " + strconv.Itoa(windowCounter), + X: rand.Intn(1000), + Y: rand.Intn(800), + BackgroundType: application.BackgroundTypeTranslucent, + HTML: ` + + + + + +
+

This is a Window with a Tabbed-effect backdrop

+
+ +`, + Windows: application.WindowsWindow{ + BackdropType: application.Tabbed, + }, + }).Show() + windowCounter++ + }) + } + + sizeMenu := menu.AddSubmenu("Size") + sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetSize(800, 600) + }) + }) + + sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200) + }) + }) + sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinSize(200, 200) + }) + }) + sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaxSize(600, 600) + w.SetMaximiseButtonState(application.ButtonDisabled) + }) + }) + sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + width, height := w.Size() + application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show() + }) + }) + + sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinSize(0, 0) + }) + }) + sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaxSize(0, 0) + w.SetMaximiseButtonState(application.ButtonEnabled) + }) + }) + + positionMenu := menu.AddSubmenu("Position") + positionMenu.Add("Set Position (0,0)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetPosition(0, 0) + }) + }) + + positionMenu.Add("Set Position (Random)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetPosition(rand.Intn(1000), rand.Intn(800)) + }) + }) + + positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + x, y := w.Position() + application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() + }) + }) + + positionMenu.Add("Set Relative Position (0,0)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetRelativePosition(0, 0) + }) + }) + positionMenu.Add("Set Relative Position (Corner)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + screen, _ := w.GetScreen() + w.SetRelativePosition(screen.WorkArea.Width-w.Width(), screen.WorkArea.Height-w.Height()) + }) + }) + positionMenu.Add("Set Relative Position (Random)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetRelativePosition(rand.Intn(1000), rand.Intn(800)) + }) + }) + + positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + x, y := w.RelativePosition() + application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() + }) + }) + + positionMenu.Add("Center").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Center() + }) + }) + titleBarMenu := menu.AddSubmenu("Controls") + titleBarMenu.Add("Disable Minimise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinimiseButtonState(application.ButtonDisabled) + }) + }) + titleBarMenu.Add("Enable Minimise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinimiseButtonState(application.ButtonEnabled) + }) + }) + titleBarMenu.Add("Hide Minimise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMinimiseButtonState(application.ButtonHidden) + }) + }) + titleBarMenu.Add("Disable Maximise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaximiseButtonState(application.ButtonDisabled) + }) + }) + titleBarMenu.Add("Enable Maximise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaximiseButtonState(application.ButtonEnabled) + }) + }) + titleBarMenu.Add("Hide Maximise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetMaximiseButtonState(application.ButtonHidden) + }) + }) + titleBarMenu.Add("Disable Close").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetCloseButtonState(application.ButtonDisabled) + }) + }) + titleBarMenu.Add("Enable Close").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetCloseButtonState(application.ButtonEnabled) + }) + }) + titleBarMenu.Add("Hide Close").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetCloseButtonState(application.ButtonHidden) + }) + }) + stateMenu := menu.AddSubmenu("State") + stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Minimise() + time.Sleep(2 * time.Second) + w.Restore() + }) + }) + stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Maximise() + }) + }) + stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Fullscreen() + }) + }) + stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.UnFullscreen() + }) + }) + stateMenu.Add("Restore").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Restore() + }) + }) + stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.Hide() + time.Sleep(2 * time.Second) + w.Show() + }) + }) + stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetAlwaysOnTop(true) + }) + }) + stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetAlwaysOnTop(false) + }) + }) + stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetURL("https://google.com") + }) + }) + stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetURL("https://wails.io") + }) + }) + stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) { + screen := app.Screen.GetPrimary() + msg := fmt.Sprintf("Screen: %+v", screen) + application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show() + }) + stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) { + screens := app.Screen.GetAll() + for _, screen := range screens { + msg := fmt.Sprintf("Screen: %+v", screen) + application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show() + } + }) + stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + screen, err := w.GetScreen() + if err != nil { + application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show() + return + } + msg := fmt.Sprintf("Screen: %+v", screen) + application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show() + }) + }) + stateMenu.Add("Disable for 5s").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SetEnabled(false) + time.Sleep(5 * time.Second) + w.SetEnabled(true) + }) + }) + stateMenu.Add("Open Dev Tools").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.OpenDevTools() + }) + }) + + if runtime.GOOS != "darwin" { + stateMenu.Add("Flash for 5s").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + time.Sleep(2 * time.Second) + w.Flash(true) + time.Sleep(5 * time.Second) + w.Flash(false) + }) + }) + } + + if runtime.GOOS == "windows" { + stateMenu.Add("Snap Assist").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + w.SnapAssist() + }) + }) + } + + printMenu := menu.AddSubmenu("Print") + printMenu.Add("Print").OnClick(func(ctx *application.Context) { + currentWindow(func(w application.Window) { + _ = w.Print() + }) + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window Demo", + BackgroundColour: application.NewRGB(33, 37, 41), + Mac: application.MacWindow{ + DisableShadow: true, + }, + Windows: application.WindowsWindow{ + Menu: menu, + }, + }) + + app.Menu.Set(menu) + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/examples/window/windows.go b/v3/examples/window/windows.go new file mode 100644 index 000000000..d66984d18 --- /dev/null +++ b/v3/examples/window/windows.go @@ -0,0 +1,11 @@ +//go:build windows + +package main + +import "github.com/wailsapp/wails/v3/pkg/w32" + +func init() { + getExStyle = func() int { + return w32.WS_EX_TOOLWINDOW | w32.WS_EX_NOREDIRECTIONBITMAP | w32.WS_EX_TOPMOST + } +} diff --git a/v3/examples/wml/README.md b/v3/examples/wml/README.md new file mode 100644 index 000000000..c8ea7850e --- /dev/null +++ b/v3/examples/wml/README.md @@ -0,0 +1,19 @@ +# WML Example + +This is an example of how to use the experimental WML, which provides HTMX style calling of the Wails JS API. + +## Running the example + +To run the example, simply run the following command: + +```bash +go run . +``` + +# Status + +| Platform | Status | +|----------|---------| +| Mac | Working | +| Windows | Working | +| Linux | | diff --git a/v3/examples/wml/assets/index.html b/v3/examples/wml/assets/index.html new file mode 100644 index 000000000..466f4c5f0 --- /dev/null +++ b/v3/examples/wml/assets/index.html @@ -0,0 +1,160 @@ + + + + + Wails Alpha + + + + + +
Alpha
+
+

Documentation

+

Feedback

+
+

This application contains no Javascript!

+

Emit event

+

Delete all the things!

+

Close the Window?

+

Center

+

Minimise

+

Maximise

+

UnMaximise

+

Fullscreen

+

UnFullscreen

+

Restore

+

Open Browser?

+

Hover over me

+
+ + + + + diff --git a/v3/examples/wml/main.go b/v3/examples/wml/main.go new file mode 100644 index 000000000..8d4a55481 --- /dev/null +++ b/v3/examples/wml/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + app := application.New(application.Options{ + Name: "Wails ML Demo", + Description: "A demo of the Wails ML API", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Wails ML Demo", + Width: 1280, + Height: 1024, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + + app.Event.On("button-pressed", func(_ *application.CustomEvent) { + println("Button Pressed!") + }) + app.Event.On("hover", func(_ *application.CustomEvent) { + println("Hover time!") + }) + + err := app.Run() + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/v3/go.mod b/v3/go.mod new file mode 100644 index 000000000..e515a8b1a --- /dev/null +++ b/v3/go.mod @@ -0,0 +1,153 @@ +module github.com/wailsapp/wails/v3 + +go 1.24.0 + +require ( + git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 + github.com/Masterminds/semver v1.5.0 + github.com/adrg/xdg v0.5.3 + github.com/atterpac/refresh v0.8.6 + github.com/bep/debounce v1.2.1 + github.com/charmbracelet/glamour v0.9.0 + github.com/ebitengine/purego v0.8.2 + github.com/go-git/go-git/v5 v5.13.2 + github.com/go-ole/go-ole v1.3.0 + github.com/godbus/dbus/v5 v5.1.0 + github.com/google/go-cmp v0.7.0 + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 + github.com/google/uuid v1.6.0 + github.com/goreleaser/nfpm/v2 v2.41.3 + github.com/jackmordaunt/icns/v2 v2.2.7 + github.com/jaypipes/ghw v0.17.0 + github.com/leaanthony/clir v1.7.0 + github.com/leaanthony/go-ansi-parser v1.6.1 + github.com/leaanthony/gosod v1.0.4 + github.com/leaanthony/u v1.1.1 + github.com/leaanthony/winicon v1.0.0 + github.com/lmittmann/tint v1.0.7 + github.com/matryer/is v1.4.1 + github.com/mattn/go-colorable v0.1.14 + github.com/mattn/go-isatty v0.0.20 + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c + github.com/pkg/errors v0.9.1 + github.com/pterm/pterm v0.12.80 + github.com/samber/lo v1.49.1 + github.com/stretchr/testify v1.10.0 + github.com/tc-hib/winres v0.3.1 + github.com/wailsapp/go-webview2 v1.0.21 + github.com/wailsapp/mimetype v1.4.1 + github.com/wailsapp/task/v3 v3.40.1-patched3 + golang.org/x/sys v0.31.0 + golang.org/x/term v0.30.0 + golang.org/x/tools v0.31.0 + gopkg.in/ini.v1 v1.67.0 + gopkg.in/yaml.v3 v3.0.1 + modernc.org/sqlite v1.36.0 +) + +require ( + atomicgo.dev/schedule v0.1.0 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) + +require ( + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + dario.cat/mergo v1.0.1 // indirect + github.com/AlekSi/pointer v1.2.0 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect + github.com/Ladicle/tabwriter v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/alecthomas/chroma/v2 v2.15.0 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect + github.com/cavaliergopher/cpio v1.0.1 // indirect + github.com/chainguard-dev/git-urls v1.0.2 // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/containerd/console v1.0.4 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/dominikbraun/graph v0.23.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-task/template v0.1.0 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/goreleaser/chglog v0.6.2 // indirect + github.com/goreleaser/fileglob v1.3.0 // indirect + github.com/gorilla/css v1.0.1 // indirect + github.com/huandu/xstrings v1.5.0 // indirect + github.com/jaypipes/pcidb v1.0.1 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-zglob v0.0.6 // indirect + github.com/microcosm-cc/bluemonday v1.0.27 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/radovskyb/watcher v1.0.7 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rjeczalik/notify v0.9.3 // indirect + github.com/sajari/fuzzy v1.0.0 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yuin/goldmark v1.7.8 // indirect + github.com/yuin/goldmark-emoji v1.0.5 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac + golang.org/x/image v0.24.0 + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/text v0.23.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + howett.net/plist v1.0.1 // indirect + modernc.org/libc v1.61.13 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.2 // indirect + mvdan.cc/sh/v3 v3.10.0 // indirect +) diff --git a/v3/go.sum b/v3/go.sum new file mode 100644 index 000000000..1f4983e7a --- /dev/null +++ b/v3/go.sum @@ -0,0 +1,466 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +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/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= +github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg= +github.com/Ladicle/tabwriter v1.0.0/go.mod h1:c4MdCjxQyTbGuQO/gvqJ+IA/89UEwrsD6hUCW98dyp4= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= +github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= +github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s= +github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= +github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/atterpac/refresh v0.8.6 h1:Q5miKV2qs9jW+USw8WZ/54Zz8/RSh/bOz5U6JvvDZmM= +github.com/atterpac/refresh v0.8.6/go.mod h1:fJpWySLdpbANS8Ej5OvfZVZIVvi/9bmnhTjKS5EjQes= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= +github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8= +github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk= +github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= +github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= +github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ= +github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/glamour v0.9.0 h1:1Hm3wxww7qXvGI+Fb3zDmIZo5oDOvVOWJ4OrIB+ef7c= +github.com/charmbracelet/glamour v0.9.0/go.mod h1:+SHvIS8qnwhgTpVMiXwn7OfGomSqff1cHBCI8jLOetk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= +github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo= +github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +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/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-task/template v0.1.0 h1:ym/r2G937RZA1bsgiWedNnY9e5kxDT+3YcoAnuIetTE= +github.com/go-task/template v0.1.0/go.mod h1:RgwRaZK+kni/hJJ7/AaOE2lPQFPbAdji/DyhC6pxo4k= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a h1:JJBdjSfqSy3mnDT0940ASQFghwcZ4y4cb6ttjAoXqwE= +github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +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/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/goreleaser/chglog v0.6.2 h1:qroqdMHzwoAPTHHzJtbCfYbwg/yWJrNQApZ6IQAq8bU= +github.com/goreleaser/chglog v0.6.2/go.mod h1:BP0xQQc6B8aM+4dhvSLlVTv0rvhuOF0JacDO1+h7L3U= +github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I= +github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU= +github.com/goreleaser/nfpm/v2 v2.41.3 h1:IRRsqv5NgiCKUy57HjQgfVBFb44VH8+r1mWeEF8OuA4= +github.com/goreleaser/nfpm/v2 v2.41.3/go.mod h1:0t54RfPX6/iKANsVLbB3XgtfQXzG1nS4HmSavN92qVY= +github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= +github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/jackmordaunt/icns/v2 v2.2.7 h1:K/RbfvuzjmjVY5y4g+XENRs8ZZatwz4YnLHypa2KwQg= +github.com/jackmordaunt/icns/v2 v2.2.7/go.mod h1:ovoTxGguSuoUGKMk5Nn3R7L7BgMQkylsO+bblBuI22A= +github.com/jaypipes/ghw v0.17.0 h1:EVLJeNcy5z6GK/Lqby0EhBpynZo+ayl8iJWY0kbEUJA= +github.com/jaypipes/ghw v0.17.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8= +github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic= +github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGFXMCeE4= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/clir v1.7.0 h1:xiAnhl7ryPwuH3ERwPWZp/pCHk8wTeiwuAOt6MiNyAw= +github.com/leaanthony/clir v1.7.0/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/leaanthony/winicon v1.0.0 h1:ZNt5U5dY71oEoKZ97UVwJRT4e+5xo5o/ieKuHuk8NqQ= +github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A= +github.com/mattn/go-zglob v0.0.6/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= +github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= +github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg= +github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo= +github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= +github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +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/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= +github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY= +github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= +github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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/tc-hib/winres v0.3.1 h1:CwRjEGrKdbi5CvZ4ID+iyVhgyfatxFoizjPhzez9Io4= +github.com/tc-hib/winres v0.3.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/task/v3 v3.40.1-patched3 h1:i6O1WNdSur9CGaiMDIYGjsmj/qS4465zqv+WEs6sPRs= +github.com/wailsapp/task/v3 v3.40.1-patched3/go.mod h1:jIP48r8ftoSQNlxFP4+aEnkvGQqQXqCnRi/B7ROaecE= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk= +github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8= +gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= +golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= +golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= +howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= +modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= +modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= +modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= +modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= +modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8= +modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= +mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= diff --git a/v3/internal/assetserver/asset_fileserver.go b/v3/internal/assetserver/asset_fileserver.go new file mode 100644 index 000000000..974bb582b --- /dev/null +++ b/v3/internal/assetserver/asset_fileserver.go @@ -0,0 +1,161 @@ +package assetserver + +import ( + "bytes" + "context" + "embed" + "errors" + "fmt" + "io" + iofs "io/fs" + "net/http" + "os" + "path" + "strings" +) + +const ( + indexHTML = "index.html" +) + +type assetFileServer struct { + fs iofs.FS + err error +} + +func newAssetFileServerFS(vfs iofs.FS) http.Handler { + subDir, err := findPathToFile(vfs, indexHTML) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + msg := "no `index.html` could be found in your Assets fs.FS" + if embedFs, isEmbedFs := vfs.(embed.FS); isEmbedFs { + rootFolder, _ := findEmbedRootPath(embedFs) + msg += fmt.Sprintf(", please make sure the embedded directory '%s' is correct and contains your assets", rootFolder) + } + + err = errors.New(msg) + } + } else { + vfs, err = iofs.Sub(vfs, path.Clean(subDir)) + } + + return &assetFileServer{fs: vfs, err: err} +} + +func (d *assetFileServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + ctx := req.Context() + url := req.URL.Path + + err := d.err + if err == nil { + filename := path.Clean(strings.TrimPrefix(url, "/")) + d.logInfo(ctx, "Handling request", "url", url, "file", filename) + err = d.serveFSFile(rw, req, filename) + if os.IsNotExist(err) { + rw.WriteHeader(http.StatusNotFound) + return + } + } + + if err != nil { + d.logError(ctx, "Unable to handle request", "url", url, "err", err) + http.Error(rw, err.Error(), http.StatusInternalServerError) + } +} + +// serveFile will try to load the file from the fs.FS and write it to the response +func (d *assetFileServer) serveFSFile(rw http.ResponseWriter, req *http.Request, filename string) error { + if d.fs == nil { + return os.ErrNotExist + } + + file, err := d.fs.Open(filename) + if err != nil { + if s := path.Ext(filename); s == "" { + filename = filename + ".html" + file, err = d.fs.Open(filename) + if err != nil { + return err + } + } else { + return err + } + } + defer file.Close() + + statInfo, err := file.Stat() + if err != nil { + return err + } + + url := req.URL.Path + isDirectoryPath := url == "" || url[len(url)-1] == '/' + if statInfo.IsDir() { + if !isDirectoryPath { + // If the URL doesn't end in a slash normally a http.redirect should be done, but that currently doesn't work on + // WebKit WebViews (macOS/Linux). + // So we handle this as a specific error + return fmt.Errorf("a directory has been requested without a trailing slash, please add a trailing slash to your request") + } + + filename = path.Join(filename, indexHTML) + + file, err = d.fs.Open(filename) + if err != nil { + return err + } + defer file.Close() + + statInfo, err = file.Stat() + if err != nil { + return err + } + } else if isDirectoryPath { + return fmt.Errorf("a file has been requested with a trailing slash, please remove the trailing slash from your request") + } + + var buf [512]byte + var n int + if _, haveType := rw.Header()[HeaderContentType]; !haveType { + // Detect MimeType by sniffing the first 512 bytes + n, err = file.Read(buf[:]) + if err != nil && err != io.EOF { + return err + } + + // Do the custom MimeType sniffing even though http.ServeContent would do it in case + // of an io.ReadSeeker. We would like to have a consistent behaviour in both cases. + if contentType := GetMimetype(filename, buf[:n]); contentType != "" { + rw.Header().Set(HeaderContentType, contentType) + } + } + + if fileSeeker, _ := file.(io.ReadSeeker); fileSeeker != nil { + if _, err := fileSeeker.Seek(0, io.SeekStart); err != nil { + return fmt.Errorf("seeker can't seek") + } + + http.ServeContent(rw, req, statInfo.Name(), statInfo.ModTime(), fileSeeker) + return nil + } + + rw.Header().Set(HeaderContentLength, fmt.Sprintf("%d", statInfo.Size())) + + // Write the first 512 bytes used for MimeType sniffing + _, err = io.Copy(rw, bytes.NewReader(buf[:n])) + if err != nil { + return err + } + + // Copy the remaining content of the file + _, err = io.Copy(rw, file) + return err +} + +func (d *assetFileServer) logInfo(ctx context.Context, message string, args ...interface{}) { + logInfo(ctx, "[AssetFileServerFS] "+message, args...) +} + +func (d *assetFileServer) logError(ctx context.Context, message string, args ...interface{}) { + logError(ctx, "[AssetFileServerFS] "+message, args...) +} diff --git a/v3/internal/assetserver/assetserver.go b/v3/internal/assetserver/assetserver.go new file mode 100644 index 000000000..6ff1b169f --- /dev/null +++ b/v3/internal/assetserver/assetserver.go @@ -0,0 +1,175 @@ +package assetserver + +import ( + "fmt" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +const ( + webViewRequestHeaderWindowId = "x-wails-window-id" + webViewRequestHeaderWindowName = "x-wails-window-name" + HeaderAcceptLanguage = "accept-language" +) + +type RuntimeHandler interface { + HandleRuntimeCall(w http.ResponseWriter, r *http.Request) +} + +type service struct { + Route string + Handler http.Handler +} + +type AssetServer struct { + options *Options + handler http.Handler + services []service + + assetServerWebView +} + +func NewAssetServer(options *Options) (*AssetServer, error) { + result := &AssetServer{ + options: options, + } + + userHandler := options.Handler + if userHandler == nil { + userHandler = http.NotFoundHandler() + } + + handler := http.Handler( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + result.serveHTTP(w, r, userHandler) + })) + + if middleware := options.Middleware; middleware != nil { + handler = middleware(handler) + } + + result.handler = handler + + return result, nil +} + +func (a *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + start := time.Now() + wrapped := newContentTypeSniffer(rw) + defer func() { + if _, err := wrapped.complete(); err != nil { + a.options.Logger.Error("Error writing response data.", "uri", req.RequestURI, "error", err) + } + }() + + req = req.WithContext(contextWithLogger(req.Context(), a.options.Logger)) + a.handler.ServeHTTP(wrapped, req) + + a.options.Logger.Info( + "Asset Request:", + "windowName", req.Header.Get(webViewRequestHeaderWindowName), + "windowID", req.Header.Get(webViewRequestHeaderWindowId), + "code", wrapped.status, + "method", req.Method, + "path", req.URL.EscapedPath(), + "duration", time.Since(start), + ) +} + +func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userHandler http.Handler) { + if isWebSocket(req) { + // WebSockets are not supported by the AssetServer + rw.WriteHeader(http.StatusNotImplemented) + return + } + + header := rw.Header() + // TODO: I don't think this is needed now? + //if a.servingFromDisk { + // header.Add(HeaderCacheControl, "no-cache") + //} + + reqPath := req.URL.Path + switch reqPath { + case "", "/", "/index.html": + // Cache the accept-language header + // before passing the request down the chain. + acceptLanguage := req.Header.Get(HeaderAcceptLanguage) + if acceptLanguage == "" { + acceptLanguage = "en" + } + + wrapped := &fallbackResponseWriter{ + rw: rw, + req: req, + fallback: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + // Set content type for default index.html + header.Set(HeaderContentType, "text/html; charset=utf-8") + a.writeBlob(rw, indexHTML, defaultIndexHTML(acceptLanguage)) + }), + } + userHandler.ServeHTTP(wrapped, req) + + default: + // Check if the path matches a service route + for _, svc := range a.services { + if strings.HasPrefix(reqPath, svc.Route) { + req.URL.Path = strings.TrimPrefix(reqPath, svc.Route) + svc.Handler.ServeHTTP(rw, req) + return + } + } + + // Forward to the user-provided handler + userHandler.ServeHTTP(rw, req) + } +} + +func (a *AssetServer) AttachServiceHandler(route string, handler http.Handler) { + a.services = append(a.services, service{route, handler}) +} + +func (a *AssetServer) writeBlob(rw http.ResponseWriter, filename string, blob []byte) { + err := ServeFile(rw, filename, blob) + if err != nil { + a.serveError(rw, err, "Error writing file content.", "filename", filename) + } +} + +func (a *AssetServer) serveError(rw http.ResponseWriter, err error, msg string, args ...interface{}) { + args = append(args, "error", err) + a.options.Logger.Error(msg, args...) + rw.WriteHeader(http.StatusInternalServerError) +} + +func GetStartURL(userURL string) (string, error) { + devServerURL := GetDevServerURL() + startURL := baseURL.String() + if devServerURL != "" { + // Parse the port + parsedURL, err := url.Parse(devServerURL) + if err != nil { + return "", fmt.Errorf("error parsing environment variable `FRONTEND_DEVSERVER_URL`: %w. Please check your `Taskfile.yml` file", err) + } + port := parsedURL.Port() + if port != "" { + baseURL.Host = net.JoinHostPort(baseURL.Hostname(), port) + startURL = baseURL.String() + } + } + + if userURL != "" { + parsedURL, err := baseURL.Parse(userURL) + if err != nil { + return "", fmt.Errorf("error parsing URL: %w", err) + } + + startURL = parsedURL.String() + } + + return startURL, nil +} diff --git a/v3/internal/assetserver/assetserver_darwin.go b/v3/internal/assetserver/assetserver_darwin.go new file mode 100644 index 000000000..faab164a4 --- /dev/null +++ b/v3/internal/assetserver/assetserver_darwin.go @@ -0,0 +1,8 @@ +package assetserver + +import "net/url" + +var baseURL = url.URL{ + Scheme: "wails", + Host: "localhost", +} diff --git a/v3/internal/assetserver/assetserver_dev.go b/v3/internal/assetserver/assetserver_dev.go new file mode 100644 index 000000000..e847ac480 --- /dev/null +++ b/v3/internal/assetserver/assetserver_dev.go @@ -0,0 +1,50 @@ +//go:build !production + +package assetserver + +import ( + "embed" + "io" + iofs "io/fs" +) + +//go:embed defaults +var defaultHTML embed.FS + +func defaultIndexHTML(language string) []byte { + result := []byte("index.html not found") + // Create an fs.Sub in the defaults directory + defaults, err := iofs.Sub(defaultHTML, "defaults") + if err != nil { + return result + } + // Get the 2 character language code + lang := "en" + if len(language) >= 2 { + lang = language[:2] + } + // Now we can read the index.html file in the format + // index..html. + + indexFile, err := defaults.Open("index." + lang + ".html") + if err != nil { + return result + } + + indexBytes, err := io.ReadAll(indexFile) + if err != nil { + return result + } + return indexBytes +} + +func (a *AssetServer) LogDetails() { + var info = []any{ + "middleware", a.options.Middleware != nil, + "handler", a.options.Handler != nil, + } + if devServerURL := GetDevServerURL(); devServerURL != "" { + info = append(info, "devServerURL", devServerURL) + } + a.options.Logger.Info("AssetServer Info:", info...) +} diff --git a/v3/internal/assetserver/assetserver_linux.go b/v3/internal/assetserver/assetserver_linux.go new file mode 100644 index 000000000..faab164a4 --- /dev/null +++ b/v3/internal/assetserver/assetserver_linux.go @@ -0,0 +1,8 @@ +package assetserver + +import "net/url" + +var baseURL = url.URL{ + Scheme: "wails", + Host: "localhost", +} diff --git a/v3/internal/assetserver/assetserver_production.go b/v3/internal/assetserver/assetserver_production.go new file mode 100644 index 000000000..f698fab40 --- /dev/null +++ b/v3/internal/assetserver/assetserver_production.go @@ -0,0 +1,9 @@ +//go:build production + +package assetserver + +func defaultIndexHTML(_ string) []byte { + return []byte("index.html not found") +} + +func (a *AssetServer) LogDetails() {} diff --git a/v3/internal/assetserver/assetserver_test.go b/v3/internal/assetserver/assetserver_test.go new file mode 100644 index 000000000..755ddf09c --- /dev/null +++ b/v3/internal/assetserver/assetserver_test.go @@ -0,0 +1,244 @@ +package assetserver + +import ( + "fmt" + "log/slog" + "net/http" + "net/http/httptest" + "strconv" + "strings" + "testing" + _ "unsafe" + + "github.com/google/go-cmp/cmp" +) + +func TestContentSniffing(t *testing.T) { + longLead := strings.Repeat(" ", 512-6) + + tests := map[string]struct { + Expect string + Status int + Header map[string][]string + Body []string + }{ + "/simple": { + Expect: "text/html; charset=utf-8", + Body: []string{"Hello!"}, + }, + "/split": { + Expect: "text/html; charset=utf-8", + Body: []string{ + "Hello!", + "", + }, + }, + "/lead/short/simple": { + Expect: "text/html; charset=utf-8", + Body: []string{ + " " + "Hello!", + }, + }, + "/lead/short/split": { + Expect: "text/html; charset=utf-8", + Body: []string{ + " ", + "Hello!", + }, + }, + "/lead/long/simple": { + Expect: "text/html; charset=utf-8", + Body: []string{ + longLead + "Hello!", + }, + }, + "/lead/long/split": { + Expect: "text/html; charset=utf-8", + Body: []string{ + longLead, + "Hello!", + }, + }, + "/lead/toolong/simple": { + Expect: "text/plain; charset=utf-8", + Body: []string{ + "Hello" + longLead + "Hello!", + }, + }, + "/lead/toolong/split": { + Expect: "text/plain; charset=utf-8", + Body: []string{ + "Hello" + longLead, + "Hello!", + }, + }, + "/header": { + Expect: "text/html; charset=utf-8", + Status: http.StatusForbidden, + Header: map[string][]string{ + "X-Custom": {"CustomValue"}, + }, + Body: []string{"Hello!"}, + }, + "/custom": { + Expect: "text/plain;charset=utf-8", + Header: map[string][]string{ + "Content-Type": {"text/plain;charset=utf-8"}, + }, + Body: []string{"Hello!"}, + }, + } + + srv, err := NewAssetServer(&Options{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + test, ok := tests[r.URL.Path] + if !ok { + w.WriteHeader(http.StatusNotFound) + return + } + + for key, values := range test.Header { + for _, value := range values { + w.Header().Add(key, value) + } + } + + if test.Status != 0 { + w.WriteHeader(test.Status) + } + + for _, chunk := range test.Body { + w.Write([]byte(chunk)) + } + }), + Logger: slog.Default(), + }) + if err != nil { + t.Fatal("AssetServer initialisation failed: ", err) + } + + for path, test := range tests { + t.Run(path[1:], func(t *testing.T) { + res := httptest.NewRecorder() + + req, err := http.NewRequest(http.MethodGet, path, nil) + if err != nil { + t.Fatal("http.NewRequest failed: ", err) + } + + srv.ServeHTTP(res, req) + + expectedStatus := http.StatusOK + if test.Status != 0 { + expectedStatus = test.Status + } + if res.Code != expectedStatus { + t.Errorf("Status code mismatch: want %d, got %d", expectedStatus, res.Code) + } + + if ct := res.Header().Get("Content-Type"); ct != test.Expect { + t.Errorf("Content type mismatch: want '%s', got '%s'", test.Expect, ct) + } + + for key, values := range test.Header { + if diff := cmp.Diff(values, res.Header().Values(key)); diff != "" { + t.Errorf("Header '%s' mismatch (-want +got):\n%s", key, diff) + } + } + + if diff := cmp.Diff(strings.Join(test.Body, ""), res.Body.String()); diff != "" { + t.Errorf("Response body mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestIndexFallback(t *testing.T) { + // Paths to try and whether a 404 should trigger a fallback. + paths := map[string]bool{ + "": true, + "/": true, + "/index": false, + "/index.html": true, + "/other": false, + } + + statuses := []int{ + http.StatusOK, + http.StatusNotFound, + http.StatusForbidden, + } + + header := map[string][]string{ + "X-Custom": {"CustomValue"}, + } + body := "Hello!" + + srv, err := NewAssetServer(&Options{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for key, values := range header { + for _, value := range values { + w.Header().Add(key, value) + } + } + + status, err := strconv.Atoi(r.URL.Query().Get("status")) + if err == nil && status != 0 && status != http.StatusOK { + w.WriteHeader(status) + } + + w.Write([]byte(body)) + }), + Logger: slog.Default(), + }) + if err != nil { + t.Fatal("AssetServer initialisation failed: ", err) + } + + for path, fallback := range paths { + for _, status := range statuses { + key := "" + if len(path) > 0 { + key = path[1:] + } + + t.Run(fmt.Sprintf("%s/status=%d", key, status), func(t *testing.T) { + res := httptest.NewRecorder() + + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s?status=%d", path, status), nil) + if err != nil { + t.Fatal("http.NewRequest failed: ", err) + } + + srv.ServeHTTP(res, req) + + fallbackTriggered := false + if status == http.StatusNotFound && fallback { + status = http.StatusOK + fallbackTriggered = true + } + + if res.Code != status { + t.Errorf("Status code mismatch: want %d, got %d", status, res.Code) + } + + if fallbackTriggered { + if cmp.Equal(body, res.Body.String()) { + t.Errorf("Fallback response has the same body as not found response") + } + return + } else { + for key, values := range header { + if diff := cmp.Diff(values, res.Header().Values(key)); diff != "" { + t.Errorf("Header '%s' mismatch (-want +got):\n%s", key, diff) + } + } + + if diff := cmp.Diff(body, res.Body.String()); diff != "" { + t.Errorf("Response body mismatch (-want +got):\n%s", diff) + } + } + }) + } + } +} diff --git a/v3/internal/assetserver/assetserver_webview.go b/v3/internal/assetserver/assetserver_webview.go new file mode 100644 index 000000000..0d029a34e --- /dev/null +++ b/v3/internal/assetserver/assetserver_webview.go @@ -0,0 +1,196 @@ +package assetserver + +import ( + "context" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + + "github.com/wailsapp/wails/v3/internal/assetserver/webview" +) + +type assetServerWebView struct { + // ExpectedWebViewHost is checked against the Request Host of every WebViewRequest, other hosts won't be processed. + ExpectedWebViewHost string + + dispatchInit sync.Once + dispatchReqC chan<- webview.Request + dispatchWorkers int +} + +// ServeWebViewRequest processes the HTTP Request asynchronously by faking a golang HTTP Server. +// The request will be finished with a StatusNotImplemented code if no handler has written to the response. +// The AssetServer takes ownership of the request and the caller mustn't close it or access it in any other way. +func (a *AssetServer) ServeWebViewRequest(req webview.Request) { + a.dispatchInit.Do(func() { + workers := a.dispatchWorkers + if workers <= 0 { + return + } + + workerC := make(chan webview.Request, workers*2) + for i := 0; i < workers; i++ { + go func() { + for req := range workerC { + a.processWebViewRequest(req) + } + }() + } + + dispatchC := make(chan webview.Request) + go queueingDispatcher(50, dispatchC, workerC) + + a.dispatchReqC = dispatchC + }) + + if a.dispatchReqC == nil { + go a.processWebViewRequest(req) + } else { + a.dispatchReqC <- req + } +} + +func (a *AssetServer) processWebViewRequest(r webview.Request) { + uri, _ := r.URL() + a.processWebViewRequestInternal(r) + if err := r.Close(); err != nil { + a.options.Logger.Error("Unable to call close for request for uri.", "uri", uri) + } +} + +// processHTTPRequest processes the HTTP Request by faking a golang HTTP Server. +// The request will be finished with a StatusNotImplemented code if no handler has written to the response. +func (a *AssetServer) processWebViewRequestInternal(r webview.Request) { + uri := "unknown" + var err error + + wrw := r.Response() + defer func() { + if err := wrw.Finish(); err != nil { + a.options.Logger.Error("Error finishing request.", "uri", uri, "error", err) + } + }() + + rw := newContentTypeSniffer(wrw) // Make sure we have a Content-Type sniffer + defer func() { + if _, err := rw.complete(); err != nil { + a.options.Logger.Error("Error writing response data.", "uri", uri, "error", err) + } + }() + defer rw.WriteHeader(http.StatusNotImplemented) // This is a NOP when a handler has already written and set the status + + uri, err = r.URL() + if err != nil { + a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("URL: %w", err)) + return + } + + method, err := r.Method() + if err != nil { + a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Method: %w", err)) + return + } + + header, err := r.Header() + if err != nil { + a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Header: %w", err)) + return + } + + body, err := r.Body() + if err != nil { + a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Body: %w", err)) + return + } + + if body == nil { + body = http.NoBody + } + defer body.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, method, uri, body) + if err != nil { + a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Request: %w", err)) + return + } + + // For server requests, the URL is parsed from the URI supplied on the Request-Line as stored in RequestURI. For + // most requests, fields other than Path and RawQuery will be empty. (See RFC 7230, Section 5.3) + req.URL.Scheme = "" + req.URL.Host = "" + req.URL.Fragment = "" + req.URL.RawFragment = "" + + if requestURL := req.URL; req.RequestURI == "" && requestURL != nil { + req.RequestURI = requestURL.String() + } + + req.Header = header + + if req.RemoteAddr == "" { + // 192.0.2.0/24 is "TEST-NET" in RFC 5737 + req.RemoteAddr = "192.0.2.1:1234" + } + + if req.RequestURI == "" && req.URL != nil { + req.RequestURI = req.URL.String() + } + + if req.ContentLength == 0 { + req.ContentLength, _ = strconv.ParseInt(req.Header.Get(HeaderContentLength), 10, 64) + } else { + req.Header.Set(HeaderContentLength, fmt.Sprintf("%d", req.ContentLength)) + } + + if host := req.Header.Get(HeaderHost); host != "" { + req.Host = host + } + + if expectedHost := a.ExpectedWebViewHost; expectedHost != "" && expectedHost != req.Host { + a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("expected host '%s' in request, but was '%s'", expectedHost, req.Host)) + return + } + + a.ServeHTTP(rw, req) +} + +func (a *AssetServer) webviewRequestErrorHandler(uri string, rw http.ResponseWriter, err error) { + logInfo := uri + if uri, err := url.ParseRequestURI(uri); err == nil { + logInfo = strings.Replace(logInfo, fmt.Sprintf("%s://%s", uri.Scheme, uri.Host), "", 1) + } + + a.options.Logger.Error("Error processing request (HttpResponse=500)", "details", logInfo, "error", err) + http.Error(rw, err.Error(), http.StatusInternalServerError) +} + +func queueingDispatcher[T any](minQueueSize uint, inC <-chan T, outC chan<- T) { + q := newRingqueue[T](minQueueSize) + for { + in, ok := <-inC + if !ok { + return + } + + q.Add(in) + for q.Len() != 0 { + out, _ := q.Peek() + select { + case outC <- out: + q.Remove() + case in, ok := <-inC: + if !ok { + return + } + + q.Add(in) + } + } + } +} diff --git a/v3/internal/assetserver/assetserver_windows.go b/v3/internal/assetserver/assetserver_windows.go new file mode 100644 index 000000000..22deda4d2 --- /dev/null +++ b/v3/internal/assetserver/assetserver_windows.go @@ -0,0 +1,8 @@ +package assetserver + +import "net/url" + +var baseURL = url.URL{ + Scheme: "http", + Host: "wails.localhost", +} diff --git a/v3/internal/assetserver/build_dev.go b/v3/internal/assetserver/build_dev.go new file mode 100644 index 000000000..7747a7142 --- /dev/null +++ b/v3/internal/assetserver/build_dev.go @@ -0,0 +1,41 @@ +//go:build !production + +package assetserver + +import ( + _ "embed" + "io/fs" + "net/http" + "net/http/httputil" + "net/url" + "os" +) + +func NewAssetFileServer(vfs fs.FS) http.Handler { + devServerURL := GetDevServerURL() + if devServerURL == "" { + return newAssetFileServerFS(vfs) + } + + parsedURL, err := url.Parse(devServerURL) + if err != nil { + return http.HandlerFunc( + func(rw http.ResponseWriter, req *http.Request) { + logError(req.Context(), "[ExternalAssetHandler] Invalid FRONTEND_DEVSERVER_URL. Should be valid URL", "error", err.Error()) + http.Error(rw, err.Error(), http.StatusInternalServerError) + }) + + } + + proxy := httputil.NewSingleHostReverseProxy(parsedURL) + proxy.ErrorHandler = func(rw http.ResponseWriter, r *http.Request, err error) { + logError(r.Context(), "[ExternalAssetHandler] Proxy error", "error", err.Error()) + rw.WriteHeader(http.StatusBadGateway) + } + + return proxy +} + +func GetDevServerURL() string { + return os.Getenv("FRONTEND_DEVSERVER_URL") +} diff --git a/v3/internal/assetserver/build_production.go b/v3/internal/assetserver/build_production.go new file mode 100644 index 000000000..98d5b4ffd --- /dev/null +++ b/v3/internal/assetserver/build_production.go @@ -0,0 +1,16 @@ +//go:build production + +package assetserver + +import ( + "io/fs" + "net/http" +) + +func NewAssetFileServer(vfs fs.FS) http.Handler { + return newAssetFileServerFS(vfs) +} + +func GetDevServerURL() string { + return "" +} diff --git a/v3/internal/assetserver/bundled_assetserver.go b/v3/internal/assetserver/bundled_assetserver.go new file mode 100644 index 000000000..15297cd37 --- /dev/null +++ b/v3/internal/assetserver/bundled_assetserver.go @@ -0,0 +1,33 @@ +package assetserver + +import ( + "github.com/wailsapp/wails/v3/internal/assetserver/bundledassets" + "io/fs" + "net/http" + "strings" +) + +type BundledAssetServer struct { + handler http.Handler +} + +func NewBundledAssetFileServer(fs fs.FS) *BundledAssetServer { + return &BundledAssetServer{ + handler: NewAssetFileServer(fs), + } +} + +func (b *BundledAssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if strings.HasPrefix(req.URL.Path, "/wails/") { + // Strip the /wails prefix + req.URL.Path = req.URL.Path[6:] + switch req.URL.Path { + case "/runtime.js": + rw.Header().Set("Content-Type", "application/javascript") + rw.Write([]byte(bundledassets.RuntimeJS)) + return + } + return + } + b.handler.ServeHTTP(rw, req) +} diff --git a/v3/internal/assetserver/bundledassets/runtime.debug.js b/v3/internal/assetserver/bundledassets/runtime.debug.js new file mode 100644 index 000000000..3e9ec0d16 --- /dev/null +++ b/v3/internal/assetserver/bundledassets/runtime.debug.js @@ -0,0 +1,2651 @@ +var __defProp = Object.defineProperty; +var __defProps = Object.defineProperties; +var __getOwnPropDescs = Object.getOwnPropertyDescriptors; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; + +// desktop/@wailsio/runtime/src/index.ts +var index_exports = {}; +__export(index_exports, { + Application: () => application_exports, + Browser: () => browser_exports, + Call: () => calls_exports, + CancelError: () => CancelError, + CancellablePromise: () => CancellablePromise, + CancelledRejectionError: () => CancelledRejectionError, + Clipboard: () => clipboard_exports, + Create: () => create_exports, + Dialogs: () => dialogs_exports, + Events: () => events_exports, + Flags: () => flags_exports, + Screens: () => screens_exports, + System: () => system_exports, + WML: () => wml_exports, + Window: () => window_default +}); + +// desktop/@wailsio/runtime/src/wml.ts +var wml_exports = {}; +__export(wml_exports, { + Enable: () => Enable, + Reload: () => Reload +}); + +// desktop/@wailsio/runtime/src/browser.ts +var browser_exports = {}; +__export(browser_exports, { + OpenURL: () => OpenURL +}); + +// desktop/@wailsio/runtime/src/nanoid.ts +var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"; +function nanoid(size = 21) { + let id = ""; + let i = size | 0; + while (i--) { + id += urlAlphabet[Math.random() * 64 | 0]; + } + return id; +} + +// desktop/@wailsio/runtime/src/runtime.ts +var runtimeURL = window.location.origin + "/wails/runtime"; +var objectNames = Object.freeze({ + Call: 0, + Clipboard: 1, + Application: 2, + Events: 3, + ContextMenu: 4, + Dialog: 5, + Window: 6, + Screens: 7, + System: 8, + Browser: 9, + CancelCall: 10 +}); +var clientId = nanoid(); +function newRuntimeCaller(object, windowName = "") { + return function(method, args = null) { + return runtimeCallWithID(object, method, windowName, args); + }; +} +async function runtimeCallWithID(objectID, method, windowName, args) { + var _a2, _b; + let url = new URL(runtimeURL); + url.searchParams.append("object", objectID.toString()); + url.searchParams.append("method", method.toString()); + if (args) { + url.searchParams.append("args", JSON.stringify(args)); + } + let headers = { + ["x-wails-client-id"]: clientId + }; + if (windowName) { + headers["x-wails-window-name"] = windowName; + } + let response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(await response.text()); + } + if (((_b = (_a2 = response.headers.get("Content-Type")) == null ? void 0 : _a2.indexOf("application/json")) != null ? _b : -1) !== -1) { + return response.json(); + } else { + return response.text(); + } +} + +// desktop/@wailsio/runtime/src/browser.ts +var call = newRuntimeCaller(objectNames.Browser); +var BrowserOpenURL = 0; +function OpenURL(url) { + return call(BrowserOpenURL, { url: url.toString() }); +} + +// desktop/@wailsio/runtime/src/dialogs.ts +var dialogs_exports = {}; +__export(dialogs_exports, { + Error: () => Error2, + Info: () => Info, + OpenFile: () => OpenFile, + Question: () => Question, + SaveFile: () => SaveFile, + Warning: () => Warning +}); +window._wails = window._wails || {}; +window._wails.dialogErrorCallback = dialogErrorCallback; +window._wails.dialogResultCallback = dialogResultCallback; +var call2 = newRuntimeCaller(objectNames.Dialog); +var dialogResponses = /* @__PURE__ */ new Map(); +var DialogInfo = 0; +var DialogWarning = 1; +var DialogError = 2; +var DialogQuestion = 3; +var DialogOpenFile = 4; +var DialogSaveFile = 5; +function dialogResultCallback(id, data, isJSON) { + let resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + if (isJSON) { + try { + resolvers.resolve(JSON.parse(data)); + } catch (err) { + resolvers.reject(new TypeError("could not parse result: " + err.message, { cause: err })); + } + } else { + resolvers.resolve(data); + } +} +function dialogErrorCallback(id, message) { + var _a2; + (_a2 = getAndDeleteResponse(id)) == null ? void 0 : _a2.reject(new window.Error(message)); +} +function getAndDeleteResponse(id) { + const response = dialogResponses.get(id); + dialogResponses.delete(id); + return response; +} +function generateID() { + let result; + do { + result = nanoid(); + } while (dialogResponses.has(result)); + return result; +} +function dialog(type, options = {}) { + const id = generateID(); + return new Promise((resolve, reject) => { + dialogResponses.set(id, { resolve, reject }); + call2(type, Object.assign({ "dialog-id": id }, options)).catch((err) => { + dialogResponses.delete(id); + reject(err); + }); + }); +} +function Info(options) { + return dialog(DialogInfo, options); +} +function Warning(options) { + return dialog(DialogWarning, options); +} +function Error2(options) { + return dialog(DialogError, options); +} +function Question(options) { + return dialog(DialogQuestion, options); +} +function OpenFile(options) { + var _a2; + return (_a2 = dialog(DialogOpenFile, options)) != null ? _a2 : []; +} +function SaveFile(options) { + return dialog(DialogSaveFile, options); +} + +// desktop/@wailsio/runtime/src/events.ts +var events_exports = {}; +__export(events_exports, { + Emit: () => Emit, + Off: () => Off, + OffAll: () => OffAll, + On: () => On, + OnMultiple: () => OnMultiple, + Once: () => Once, + Types: () => Types, + WailsEvent: () => WailsEvent +}); + +// desktop/@wailsio/runtime/src/listener.ts +var eventListeners = /* @__PURE__ */ new Map(); +var Listener = class { + constructor(eventName, callback, maxCallbacks) { + this.eventName = eventName; + this.callback = callback; + this.maxCallbacks = maxCallbacks || -1; + } + dispatch(data) { + try { + this.callback(data); + } catch (err) { + console.error(err); + } + if (this.maxCallbacks === -1) return false; + this.maxCallbacks -= 1; + return this.maxCallbacks === 0; + } +}; +function listenerOff(listener) { + let listeners = eventListeners.get(listener.eventName); + if (!listeners) { + return; + } + listeners = listeners.filter((l) => l !== listener); + if (listeners.length === 0) { + eventListeners.delete(listener.eventName); + } else { + eventListeners.set(listener.eventName, listeners); + } +} + +// desktop/@wailsio/runtime/src/event_types.ts +var Types = Object.freeze({ + Windows: Object.freeze({ + APMPowerSettingChange: "windows:APMPowerSettingChange", + APMPowerStatusChange: "windows:APMPowerStatusChange", + APMResumeAutomatic: "windows:APMResumeAutomatic", + APMResumeSuspend: "windows:APMResumeSuspend", + APMSuspend: "windows:APMSuspend", + ApplicationStarted: "windows:ApplicationStarted", + SystemThemeChanged: "windows:SystemThemeChanged", + WebViewNavigationCompleted: "windows:WebViewNavigationCompleted", + WindowActive: "windows:WindowActive", + WindowBackgroundErase: "windows:WindowBackgroundErase", + WindowClickActive: "windows:WindowClickActive", + WindowClosing: "windows:WindowClosing", + WindowDidMove: "windows:WindowDidMove", + WindowDidResize: "windows:WindowDidResize", + WindowDPIChanged: "windows:WindowDPIChanged", + WindowDragDrop: "windows:WindowDragDrop", + WindowDragEnter: "windows:WindowDragEnter", + WindowDragLeave: "windows:WindowDragLeave", + WindowDragOver: "windows:WindowDragOver", + WindowEndMove: "windows:WindowEndMove", + WindowEndResize: "windows:WindowEndResize", + WindowFullscreen: "windows:WindowFullscreen", + WindowHide: "windows:WindowHide", + WindowInactive: "windows:WindowInactive", + WindowKeyDown: "windows:WindowKeyDown", + WindowKeyUp: "windows:WindowKeyUp", + WindowKillFocus: "windows:WindowKillFocus", + WindowNonClientHit: "windows:WindowNonClientHit", + WindowNonClientMouseDown: "windows:WindowNonClientMouseDown", + WindowNonClientMouseLeave: "windows:WindowNonClientMouseLeave", + WindowNonClientMouseMove: "windows:WindowNonClientMouseMove", + WindowNonClientMouseUp: "windows:WindowNonClientMouseUp", + WindowPaint: "windows:WindowPaint", + WindowRestore: "windows:WindowRestore", + WindowSetFocus: "windows:WindowSetFocus", + WindowShow: "windows:WindowShow", + WindowStartMove: "windows:WindowStartMove", + WindowStartResize: "windows:WindowStartResize", + WindowUnFullscreen: "windows:WindowUnFullscreen", + WindowZOrderChanged: "windows:WindowZOrderChanged", + WindowMinimise: "windows:WindowMinimise", + WindowUnMinimise: "windows:WindowUnMinimise", + WindowMaximise: "windows:WindowMaximise", + WindowUnMaximise: "windows:WindowUnMaximise" + }), + Mac: Object.freeze({ + ApplicationDidBecomeActive: "mac:ApplicationDidBecomeActive", + ApplicationDidChangeBackingProperties: "mac:ApplicationDidChangeBackingProperties", + ApplicationDidChangeEffectiveAppearance: "mac:ApplicationDidChangeEffectiveAppearance", + ApplicationDidChangeIcon: "mac:ApplicationDidChangeIcon", + ApplicationDidChangeOcclusionState: "mac:ApplicationDidChangeOcclusionState", + ApplicationDidChangeScreenParameters: "mac:ApplicationDidChangeScreenParameters", + ApplicationDidChangeStatusBarFrame: "mac:ApplicationDidChangeStatusBarFrame", + ApplicationDidChangeStatusBarOrientation: "mac:ApplicationDidChangeStatusBarOrientation", + ApplicationDidChangeTheme: "mac:ApplicationDidChangeTheme", + ApplicationDidFinishLaunching: "mac:ApplicationDidFinishLaunching", + ApplicationDidHide: "mac:ApplicationDidHide", + ApplicationDidResignActive: "mac:ApplicationDidResignActive", + ApplicationDidUnhide: "mac:ApplicationDidUnhide", + ApplicationDidUpdate: "mac:ApplicationDidUpdate", + ApplicationShouldHandleReopen: "mac:ApplicationShouldHandleReopen", + ApplicationWillBecomeActive: "mac:ApplicationWillBecomeActive", + ApplicationWillFinishLaunching: "mac:ApplicationWillFinishLaunching", + ApplicationWillHide: "mac:ApplicationWillHide", + ApplicationWillResignActive: "mac:ApplicationWillResignActive", + ApplicationWillTerminate: "mac:ApplicationWillTerminate", + ApplicationWillUnhide: "mac:ApplicationWillUnhide", + ApplicationWillUpdate: "mac:ApplicationWillUpdate", + MenuDidAddItem: "mac:MenuDidAddItem", + MenuDidBeginTracking: "mac:MenuDidBeginTracking", + MenuDidClose: "mac:MenuDidClose", + MenuDidDisplayItem: "mac:MenuDidDisplayItem", + MenuDidEndTracking: "mac:MenuDidEndTracking", + MenuDidHighlightItem: "mac:MenuDidHighlightItem", + MenuDidOpen: "mac:MenuDidOpen", + MenuDidPopUp: "mac:MenuDidPopUp", + MenuDidRemoveItem: "mac:MenuDidRemoveItem", + MenuDidSendAction: "mac:MenuDidSendAction", + MenuDidSendActionToItem: "mac:MenuDidSendActionToItem", + MenuDidUpdate: "mac:MenuDidUpdate", + MenuWillAddItem: "mac:MenuWillAddItem", + MenuWillBeginTracking: "mac:MenuWillBeginTracking", + MenuWillDisplayItem: "mac:MenuWillDisplayItem", + MenuWillEndTracking: "mac:MenuWillEndTracking", + MenuWillHighlightItem: "mac:MenuWillHighlightItem", + MenuWillOpen: "mac:MenuWillOpen", + MenuWillPopUp: "mac:MenuWillPopUp", + MenuWillRemoveItem: "mac:MenuWillRemoveItem", + MenuWillSendAction: "mac:MenuWillSendAction", + MenuWillSendActionToItem: "mac:MenuWillSendActionToItem", + MenuWillUpdate: "mac:MenuWillUpdate", + WebViewDidCommitNavigation: "mac:WebViewDidCommitNavigation", + WebViewDidFinishNavigation: "mac:WebViewDidFinishNavigation", + WebViewDidReceiveServerRedirectForProvisionalNavigation: "mac:WebViewDidReceiveServerRedirectForProvisionalNavigation", + WebViewDidStartProvisionalNavigation: "mac:WebViewDidStartProvisionalNavigation", + WindowDidBecomeKey: "mac:WindowDidBecomeKey", + WindowDidBecomeMain: "mac:WindowDidBecomeMain", + WindowDidBeginSheet: "mac:WindowDidBeginSheet", + WindowDidChangeAlpha: "mac:WindowDidChangeAlpha", + WindowDidChangeBackingLocation: "mac:WindowDidChangeBackingLocation", + WindowDidChangeBackingProperties: "mac:WindowDidChangeBackingProperties", + WindowDidChangeCollectionBehavior: "mac:WindowDidChangeCollectionBehavior", + WindowDidChangeEffectiveAppearance: "mac:WindowDidChangeEffectiveAppearance", + WindowDidChangeOcclusionState: "mac:WindowDidChangeOcclusionState", + WindowDidChangeOrderingMode: "mac:WindowDidChangeOrderingMode", + WindowDidChangeScreen: "mac:WindowDidChangeScreen", + WindowDidChangeScreenParameters: "mac:WindowDidChangeScreenParameters", + WindowDidChangeScreenProfile: "mac:WindowDidChangeScreenProfile", + WindowDidChangeScreenSpace: "mac:WindowDidChangeScreenSpace", + WindowDidChangeScreenSpaceProperties: "mac:WindowDidChangeScreenSpaceProperties", + WindowDidChangeSharingType: "mac:WindowDidChangeSharingType", + WindowDidChangeSpace: "mac:WindowDidChangeSpace", + WindowDidChangeSpaceOrderingMode: "mac:WindowDidChangeSpaceOrderingMode", + WindowDidChangeTitle: "mac:WindowDidChangeTitle", + WindowDidChangeToolbar: "mac:WindowDidChangeToolbar", + WindowDidDeminiaturize: "mac:WindowDidDeminiaturize", + WindowDidEndSheet: "mac:WindowDidEndSheet", + WindowDidEnterFullScreen: "mac:WindowDidEnterFullScreen", + WindowDidEnterVersionBrowser: "mac:WindowDidEnterVersionBrowser", + WindowDidExitFullScreen: "mac:WindowDidExitFullScreen", + WindowDidExitVersionBrowser: "mac:WindowDidExitVersionBrowser", + WindowDidExpose: "mac:WindowDidExpose", + WindowDidFocus: "mac:WindowDidFocus", + WindowDidMiniaturize: "mac:WindowDidMiniaturize", + WindowDidMove: "mac:WindowDidMove", + WindowDidOrderOffScreen: "mac:WindowDidOrderOffScreen", + WindowDidOrderOnScreen: "mac:WindowDidOrderOnScreen", + WindowDidResignKey: "mac:WindowDidResignKey", + WindowDidResignMain: "mac:WindowDidResignMain", + WindowDidResize: "mac:WindowDidResize", + WindowDidUpdate: "mac:WindowDidUpdate", + WindowDidUpdateAlpha: "mac:WindowDidUpdateAlpha", + WindowDidUpdateCollectionBehavior: "mac:WindowDidUpdateCollectionBehavior", + WindowDidUpdateCollectionProperties: "mac:WindowDidUpdateCollectionProperties", + WindowDidUpdateShadow: "mac:WindowDidUpdateShadow", + WindowDidUpdateTitle: "mac:WindowDidUpdateTitle", + WindowDidUpdateToolbar: "mac:WindowDidUpdateToolbar", + WindowDidZoom: "mac:WindowDidZoom", + WindowFileDraggingEntered: "mac:WindowFileDraggingEntered", + WindowFileDraggingExited: "mac:WindowFileDraggingExited", + WindowFileDraggingPerformed: "mac:WindowFileDraggingPerformed", + WindowHide: "mac:WindowHide", + WindowMaximise: "mac:WindowMaximise", + WindowUnMaximise: "mac:WindowUnMaximise", + WindowMinimise: "mac:WindowMinimise", + WindowUnMinimise: "mac:WindowUnMinimise", + WindowShouldClose: "mac:WindowShouldClose", + WindowShow: "mac:WindowShow", + WindowWillBecomeKey: "mac:WindowWillBecomeKey", + WindowWillBecomeMain: "mac:WindowWillBecomeMain", + WindowWillBeginSheet: "mac:WindowWillBeginSheet", + WindowWillChangeOrderingMode: "mac:WindowWillChangeOrderingMode", + WindowWillClose: "mac:WindowWillClose", + WindowWillDeminiaturize: "mac:WindowWillDeminiaturize", + WindowWillEnterFullScreen: "mac:WindowWillEnterFullScreen", + WindowWillEnterVersionBrowser: "mac:WindowWillEnterVersionBrowser", + WindowWillExitFullScreen: "mac:WindowWillExitFullScreen", + WindowWillExitVersionBrowser: "mac:WindowWillExitVersionBrowser", + WindowWillFocus: "mac:WindowWillFocus", + WindowWillMiniaturize: "mac:WindowWillMiniaturize", + WindowWillMove: "mac:WindowWillMove", + WindowWillOrderOffScreen: "mac:WindowWillOrderOffScreen", + WindowWillOrderOnScreen: "mac:WindowWillOrderOnScreen", + WindowWillResignMain: "mac:WindowWillResignMain", + WindowWillResize: "mac:WindowWillResize", + WindowWillUnfocus: "mac:WindowWillUnfocus", + WindowWillUpdate: "mac:WindowWillUpdate", + WindowWillUpdateAlpha: "mac:WindowWillUpdateAlpha", + WindowWillUpdateCollectionBehavior: "mac:WindowWillUpdateCollectionBehavior", + WindowWillUpdateCollectionProperties: "mac:WindowWillUpdateCollectionProperties", + WindowWillUpdateShadow: "mac:WindowWillUpdateShadow", + WindowWillUpdateTitle: "mac:WindowWillUpdateTitle", + WindowWillUpdateToolbar: "mac:WindowWillUpdateToolbar", + WindowWillUpdateVisibility: "mac:WindowWillUpdateVisibility", + WindowWillUseStandardFrame: "mac:WindowWillUseStandardFrame", + WindowZoomIn: "mac:WindowZoomIn", + WindowZoomOut: "mac:WindowZoomOut", + WindowZoomReset: "mac:WindowZoomReset" + }), + Linux: Object.freeze({ + ApplicationStartup: "linux:ApplicationStartup", + SystemThemeChanged: "linux:SystemThemeChanged", + WindowDeleteEvent: "linux:WindowDeleteEvent", + WindowDidMove: "linux:WindowDidMove", + WindowDidResize: "linux:WindowDidResize", + WindowFocusIn: "linux:WindowFocusIn", + WindowFocusOut: "linux:WindowFocusOut", + WindowLoadChanged: "linux:WindowLoadChanged" + }), + Common: Object.freeze({ + ApplicationOpenedWithFile: "common:ApplicationOpenedWithFile", + ApplicationStarted: "common:ApplicationStarted", + ApplicationLaunchedWithUrl: "common:ApplicationLaunchedWithUrl", + ThemeChanged: "common:ThemeChanged", + WindowClosing: "common:WindowClosing", + WindowDidMove: "common:WindowDidMove", + WindowDidResize: "common:WindowDidResize", + WindowDPIChanged: "common:WindowDPIChanged", + WindowFilesDropped: "common:WindowFilesDropped", + WindowFocus: "common:WindowFocus", + WindowFullscreen: "common:WindowFullscreen", + WindowHide: "common:WindowHide", + WindowLostFocus: "common:WindowLostFocus", + WindowMaximise: "common:WindowMaximise", + WindowMinimise: "common:WindowMinimise", + WindowToggleFrameless: "common:WindowToggleFrameless", + WindowRestore: "common:WindowRestore", + WindowRuntimeReady: "common:WindowRuntimeReady", + WindowShow: "common:WindowShow", + WindowUnFullscreen: "common:WindowUnFullscreen", + WindowUnMaximise: "common:WindowUnMaximise", + WindowUnMinimise: "common:WindowUnMinimise", + WindowZoom: "common:WindowZoom", + WindowZoomIn: "common:WindowZoomIn", + WindowZoomOut: "common:WindowZoomOut", + WindowZoomReset: "common:WindowZoomReset", + WindowDropZoneFilesDropped: "common:WindowDropZoneFilesDropped" + }) +}); + +// desktop/@wailsio/runtime/src/events.ts +window._wails = window._wails || {}; +window._wails.dispatchWailsEvent = dispatchWailsEvent; +var call3 = newRuntimeCaller(objectNames.Events); +var EmitMethod = 0; +var WailsEvent = class { + constructor(name, data = null) { + this.name = name; + this.data = data; + } +}; +function dispatchWailsEvent(event) { + let listeners = eventListeners.get(event.name); + if (!listeners) { + return; + } + let wailsEvent = new WailsEvent(event.name, event.data); + if ("sender" in event) { + wailsEvent.sender = event.sender; + } + listeners = listeners.filter((listener) => !listener.dispatch(wailsEvent)); + if (listeners.length === 0) { + eventListeners.delete(event.name); + } else { + eventListeners.set(event.name, listeners); + } +} +function OnMultiple(eventName, callback, maxCallbacks) { + let listeners = eventListeners.get(eventName) || []; + const thisListener = new Listener(eventName, callback, maxCallbacks); + listeners.push(thisListener); + eventListeners.set(eventName, listeners); + return () => listenerOff(thisListener); +} +function On(eventName, callback) { + return OnMultiple(eventName, callback, -1); +} +function Once(eventName, callback) { + return OnMultiple(eventName, callback, 1); +} +function Off(...eventNames) { + eventNames.forEach((eventName) => eventListeners.delete(eventName)); +} +function OffAll() { + eventListeners.clear(); +} +function Emit(name, data) { + let eventName; + let eventData; + if (typeof name === "object" && name !== null && "name" in name && "data" in name) { + eventName = name["name"]; + eventData = name["data"]; + } else { + eventName = name; + eventData = data; + } + return call3(EmitMethod, { name: eventName, data: eventData }); +} + +// desktop/@wailsio/runtime/src/utils.ts +function debugLog(message) { + console.log( + "%c wails3 %c " + message + " ", + "background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem", + "background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem" + ); +} +function canTrackButtons() { + return new MouseEvent("mousedown").buttons === 0; +} +function canAbortListeners() { + if (!EventTarget || !AbortSignal || !AbortController) + return false; + let result = true; + const target = new EventTarget(); + const controller = new AbortController(); + target.addEventListener("test", () => { + result = false; + }, { signal: controller.signal }); + controller.abort(); + target.dispatchEvent(new CustomEvent("test")); + return result; +} +function eventTarget(event) { + var _a2; + if (event.target instanceof HTMLElement) { + return event.target; + } else if (!(event.target instanceof HTMLElement) && event.target instanceof Node) { + return (_a2 = event.target.parentElement) != null ? _a2 : document.body; + } else { + return document.body; + } +} +var isReady = false; +document.addEventListener("DOMContentLoaded", () => { + isReady = true; +}); +function whenReady(callback) { + if (isReady || document.readyState === "complete") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +} + +// desktop/@wailsio/runtime/src/window.ts +var DROPZONE_ATTRIBUTE = "data-wails-dropzone"; +var DROPZONE_HOVER_CLASS = "wails-dropzone-hover"; +var currentHoveredDropzone = null; +var PositionMethod = 0; +var CenterMethod = 1; +var CloseMethod = 2; +var DisableSizeConstraintsMethod = 3; +var EnableSizeConstraintsMethod = 4; +var FocusMethod = 5; +var ForceReloadMethod = 6; +var FullscreenMethod = 7; +var GetScreenMethod = 8; +var GetZoomMethod = 9; +var HeightMethod = 10; +var HideMethod = 11; +var IsFocusedMethod = 12; +var IsFullscreenMethod = 13; +var IsMaximisedMethod = 14; +var IsMinimisedMethod = 15; +var MaximiseMethod = 16; +var MinimiseMethod = 17; +var NameMethod = 18; +var OpenDevToolsMethod = 19; +var RelativePositionMethod = 20; +var ReloadMethod = 21; +var ResizableMethod = 22; +var RestoreMethod = 23; +var SetPositionMethod = 24; +var SetAlwaysOnTopMethod = 25; +var SetBackgroundColourMethod = 26; +var SetFramelessMethod = 27; +var SetFullscreenButtonEnabledMethod = 28; +var SetMaxSizeMethod = 29; +var SetMinSizeMethod = 30; +var SetRelativePositionMethod = 31; +var SetResizableMethod = 32; +var SetSizeMethod = 33; +var SetTitleMethod = 34; +var SetZoomMethod = 35; +var ShowMethod = 36; +var SizeMethod = 37; +var ToggleFullscreenMethod = 38; +var ToggleMaximiseMethod = 39; +var ToggleFramelessMethod = 40; +var UnFullscreenMethod = 41; +var UnMaximiseMethod = 42; +var UnMinimiseMethod = 43; +var WidthMethod = 44; +var ZoomMethod = 45; +var ZoomInMethod = 46; +var ZoomOutMethod = 47; +var ZoomResetMethod = 48; +var WindowDropZoneDropped = 49; +function getDropzoneElement(element) { + if (!element) { + return null; + } + return element.closest("[".concat(DROPZONE_ATTRIBUTE, "]")); +} +var callerSym = Symbol("caller"); +callerSym; +var _Window = class _Window { + /** + * Initialises a window object with the specified name. + * + * @private + * @param name - The name of the target window. + */ + constructor(name = "") { + this[callerSym] = newRuntimeCaller(objectNames.Window, name); + for (const method of Object.getOwnPropertyNames(_Window.prototype)) { + if (method !== "constructor" && typeof this[method] === "function") { + this[method] = this[method].bind(this); + } + } + } + /** + * Gets the specified window. + * + * @param name - The name of the window to get. + * @returns The corresponding window object. + */ + Get(name) { + return new _Window(name); + } + /** + * Returns the absolute position of the window. + * + * @returns The current absolute position of the window. + */ + Position() { + return this[callerSym](PositionMethod); + } + /** + * Centers the window on the screen. + */ + Center() { + return this[callerSym](CenterMethod); + } + /** + * Closes the window. + */ + Close() { + return this[callerSym](CloseMethod); + } + /** + * Disables min/max size constraints. + */ + DisableSizeConstraints() { + return this[callerSym](DisableSizeConstraintsMethod); + } + /** + * Enables min/max size constraints. + */ + EnableSizeConstraints() { + return this[callerSym](EnableSizeConstraintsMethod); + } + /** + * Focuses the window. + */ + Focus() { + return this[callerSym](FocusMethod); + } + /** + * Forces the window to reload the page assets. + */ + ForceReload() { + return this[callerSym](ForceReloadMethod); + } + /** + * Switches the window to fullscreen mode. + */ + Fullscreen() { + return this[callerSym](FullscreenMethod); + } + /** + * Returns the screen that the window is on. + * + * @returns The screen the window is currently on. + */ + GetScreen() { + return this[callerSym](GetScreenMethod); + } + /** + * Returns the current zoom level of the window. + * + * @returns The current zoom level. + */ + GetZoom() { + return this[callerSym](GetZoomMethod); + } + /** + * Returns the height of the window. + * + * @returns The current height of the window. + */ + Height() { + return this[callerSym](HeightMethod); + } + /** + * Hides the window. + */ + Hide() { + return this[callerSym](HideMethod); + } + /** + * Returns true if the window is focused. + * + * @returns Whether the window is currently focused. + */ + IsFocused() { + return this[callerSym](IsFocusedMethod); + } + /** + * Returns true if the window is fullscreen. + * + * @returns Whether the window is currently fullscreen. + */ + IsFullscreen() { + return this[callerSym](IsFullscreenMethod); + } + /** + * Returns true if the window is maximised. + * + * @returns Whether the window is currently maximised. + */ + IsMaximised() { + return this[callerSym](IsMaximisedMethod); + } + /** + * Returns true if the window is minimised. + * + * @returns Whether the window is currently minimised. + */ + IsMinimised() { + return this[callerSym](IsMinimisedMethod); + } + /** + * Maximises the window. + */ + Maximise() { + return this[callerSym](MaximiseMethod); + } + /** + * Minimises the window. + */ + Minimise() { + return this[callerSym](MinimiseMethod); + } + /** + * Returns the name of the window. + * + * @returns The name of the window. + */ + Name() { + return this[callerSym](NameMethod); + } + /** + * Opens the development tools pane. + */ + OpenDevTools() { + return this[callerSym](OpenDevToolsMethod); + } + /** + * Returns the relative position of the window to the screen. + * + * @returns The current relative position of the window. + */ + RelativePosition() { + return this[callerSym](RelativePositionMethod); + } + /** + * Reloads the page assets. + */ + Reload() { + return this[callerSym](ReloadMethod); + } + /** + * Returns true if the window is resizable. + * + * @returns Whether the window is currently resizable. + */ + Resizable() { + return this[callerSym](ResizableMethod); + } + /** + * Restores the window to its previous state if it was previously minimised, maximised or fullscreen. + */ + Restore() { + return this[callerSym](RestoreMethod); + } + /** + * Sets the absolute position of the window. + * + * @param x - The desired horizontal absolute position of the window. + * @param y - The desired vertical absolute position of the window. + */ + SetPosition(x, y) { + return this[callerSym](SetPositionMethod, { x, y }); + } + /** + * Sets the window to be always on top. + * + * @param alwaysOnTop - Whether the window should stay on top. + */ + SetAlwaysOnTop(alwaysOnTop) { + return this[callerSym](SetAlwaysOnTopMethod, { alwaysOnTop }); + } + /** + * Sets the background colour of the window. + * + * @param r - The desired red component of the window background. + * @param g - The desired green component of the window background. + * @param b - The desired blue component of the window background. + * @param a - The desired alpha component of the window background. + */ + SetBackgroundColour(r, g, b, a) { + return this[callerSym](SetBackgroundColourMethod, { r, g, b, a }); + } + /** + * Removes the window frame and title bar. + * + * @param frameless - Whether the window should be frameless. + */ + SetFrameless(frameless) { + return this[callerSym](SetFramelessMethod, { frameless }); + } + /** + * Disables the system fullscreen button. + * + * @param enabled - Whether the fullscreen button should be enabled. + */ + SetFullscreenButtonEnabled(enabled) { + return this[callerSym](SetFullscreenButtonEnabledMethod, { enabled }); + } + /** + * Sets the maximum size of the window. + * + * @param width - The desired maximum width of the window. + * @param height - The desired maximum height of the window. + */ + SetMaxSize(width, height) { + return this[callerSym](SetMaxSizeMethod, { width, height }); + } + /** + * Sets the minimum size of the window. + * + * @param width - The desired minimum width of the window. + * @param height - The desired minimum height of the window. + */ + SetMinSize(width, height) { + return this[callerSym](SetMinSizeMethod, { width, height }); + } + /** + * Sets the relative position of the window to the screen. + * + * @param x - The desired horizontal relative position of the window. + * @param y - The desired vertical relative position of the window. + */ + SetRelativePosition(x, y) { + return this[callerSym](SetRelativePositionMethod, { x, y }); + } + /** + * Sets whether the window is resizable. + * + * @param resizable - Whether the window should be resizable. + */ + SetResizable(resizable2) { + return this[callerSym](SetResizableMethod, { resizable: resizable2 }); + } + /** + * Sets the size of the window. + * + * @param width - The desired width of the window. + * @param height - The desired height of the window. + */ + SetSize(width, height) { + return this[callerSym](SetSizeMethod, { width, height }); + } + /** + * Sets the title of the window. + * + * @param title - The desired title of the window. + */ + SetTitle(title) { + return this[callerSym](SetTitleMethod, { title }); + } + /** + * Sets the zoom level of the window. + * + * @param zoom - The desired zoom level. + */ + SetZoom(zoom) { + return this[callerSym](SetZoomMethod, { zoom }); + } + /** + * Shows the window. + */ + Show() { + return this[callerSym](ShowMethod); + } + /** + * Returns the size of the window. + * + * @returns The current size of the window. + */ + Size() { + return this[callerSym](SizeMethod); + } + /** + * Toggles the window between fullscreen and normal. + */ + ToggleFullscreen() { + return this[callerSym](ToggleFullscreenMethod); + } + /** + * Toggles the window between maximised and normal. + */ + ToggleMaximise() { + return this[callerSym](ToggleMaximiseMethod); + } + /** + * Toggles the window between frameless and normal. + */ + ToggleFrameless() { + return this[callerSym](ToggleFramelessMethod); + } + /** + * Un-fullscreens the window. + */ + UnFullscreen() { + return this[callerSym](UnFullscreenMethod); + } + /** + * Un-maximises the window. + */ + UnMaximise() { + return this[callerSym](UnMaximiseMethod); + } + /** + * Un-minimises the window. + */ + UnMinimise() { + return this[callerSym](UnMinimiseMethod); + } + /** + * Returns the width of the window. + * + * @returns The current width of the window. + */ + Width() { + return this[callerSym](WidthMethod); + } + /** + * Zooms the window. + */ + Zoom() { + return this[callerSym](ZoomMethod); + } + /** + * Increases the zoom level of the webview content. + */ + ZoomIn() { + return this[callerSym](ZoomInMethod); + } + /** + * Decreases the zoom level of the webview content. + */ + ZoomOut() { + return this[callerSym](ZoomOutMethod); + } + /** + * Resets the zoom level of the webview content. + */ + ZoomReset() { + return this[callerSym](ZoomResetMethod); + } + /** + * Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop). + * Gathers information about the drop target element and sends it back to the Go backend. + * + * @param filenames - An array of file paths (strings) that were dropped. + * @param x - The x-coordinate of the drop event. + * @param y - The y-coordinate of the drop event. + */ + HandlePlatformFileDrop(filenames, x, y) { + const element = document.elementFromPoint(x, y); + const dropzoneTarget = getDropzoneElement(element); + if (!dropzoneTarget) { + console.log("Wails Runtime: Drop on element (or no element) at ".concat(x, ",").concat(y, " which is not a designated dropzone. Ignoring. Element:"), element); + return; + } + console.log("Wails Runtime: Drop on designated dropzone. Element at (".concat(x, ", ").concat(y, "):"), element, "Effective dropzone:", dropzoneTarget); + const elementDetails = { + id: dropzoneTarget.id, + classList: Array.from(dropzoneTarget.classList), + attributes: {} + }; + for (let i = 0; i < dropzoneTarget.attributes.length; i++) { + const attr = dropzoneTarget.attributes[i]; + elementDetails.attributes[attr.name] = attr.value; + } + const payload = { + filenames, + x, + y, + elementDetails + }; + this[callerSym](WindowDropZoneDropped, payload); + } +}; +var Window = _Window; +var thisWindow = new Window(""); +function setupGlobalDropzoneListeners() { + const docElement = document.documentElement; + let dragEnterCounter = 0; + docElement.addEventListener("dragenter", (event) => { + event.preventDefault(); + if (event.dataTransfer && event.dataTransfer.types.includes("Files")) { + dragEnterCounter++; + const targetElement = document.elementFromPoint(event.clientX, event.clientY); + const dropzone = getDropzoneElement(targetElement); + if (currentHoveredDropzone && currentHoveredDropzone !== dropzone) { + currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS); + } + if (dropzone) { + dropzone.classList.add(DROPZONE_HOVER_CLASS); + event.dataTransfer.dropEffect = "copy"; + currentHoveredDropzone = dropzone; + } else { + event.dataTransfer.dropEffect = "none"; + currentHoveredDropzone = null; + } + } + }, false); + docElement.addEventListener("dragover", (event) => { + event.preventDefault(); + if (event.dataTransfer && event.dataTransfer.types.includes("Files")) { + if (currentHoveredDropzone) { + if (!currentHoveredDropzone.classList.contains(DROPZONE_HOVER_CLASS)) { + currentHoveredDropzone.classList.add(DROPZONE_HOVER_CLASS); + } + event.dataTransfer.dropEffect = "copy"; + } else { + event.dataTransfer.dropEffect = "none"; + } + } + }, false); + docElement.addEventListener("dragleave", (event) => { + event.preventDefault(); + if (event.dataTransfer && event.dataTransfer.types.includes("Files")) { + dragEnterCounter--; + if (dragEnterCounter === 0 || event.relatedTarget === null || currentHoveredDropzone && !currentHoveredDropzone.contains(event.relatedTarget)) { + if (currentHoveredDropzone) { + currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS); + currentHoveredDropzone = null; + } + dragEnterCounter = 0; + } + } + }, false); + docElement.addEventListener("drop", (event) => { + event.preventDefault(); + dragEnterCounter = 0; + if (currentHoveredDropzone) { + currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS); + currentHoveredDropzone = null; + } + }, false); +} +if (typeof window !== "undefined" && typeof document !== "undefined") { + setupGlobalDropzoneListeners(); +} +var window_default = thisWindow; + +// desktop/@wailsio/runtime/src/wml.ts +function sendEvent(eventName, data = null) { + Emit(new WailsEvent(eventName, data)); +} +function callWindowMethod(windowName, methodName) { + const targetWindow = window_default.Get(windowName); + const method = targetWindow[methodName]; + if (typeof method !== "function") { + console.error("Window method '".concat(methodName, "' not found")); + return; + } + try { + method.call(targetWindow); + } catch (e) { + console.error("Error calling window method '".concat(methodName, "': "), e); + } +} +function onWMLTriggered(ev) { + const element = ev.currentTarget; + function runEffect(choice = "Yes") { + if (choice !== "Yes") + return; + const eventType = element.getAttribute("wml-event") || element.getAttribute("data-wml-event"); + const targetWindow = element.getAttribute("wml-target-window") || element.getAttribute("data-wml-target-window") || ""; + const windowMethod = element.getAttribute("wml-window") || element.getAttribute("data-wml-window"); + const url = element.getAttribute("wml-openurl") || element.getAttribute("data-wml-openurl"); + if (eventType !== null) + sendEvent(eventType); + if (windowMethod !== null) + callWindowMethod(targetWindow, windowMethod); + if (url !== null) + void OpenURL(url); + } + const confirm = element.getAttribute("wml-confirm") || element.getAttribute("data-wml-confirm"); + if (confirm) { + Question({ + Title: "Confirm", + Message: confirm, + Detached: false, + Buttons: [ + { Label: "Yes" }, + { Label: "No", IsDefault: true } + ] + }).then(runEffect); + } else { + runEffect(); + } +} +var controllerSym = Symbol("controller"); +var triggerMapSym = Symbol("triggerMap"); +var elementCountSym = Symbol("elementCount"); +controllerSym; +var AbortControllerRegistry = class { + constructor() { + this[controllerSym] = new AbortController(); + } + /** + * Returns an options object for addEventListener that ties the listener + * to the AbortSignal from the current AbortController. + * + * @param element - An HTML element + * @param triggers - The list of active WML trigger events for the specified elements + */ + set(element, triggers) { + return { signal: this[controllerSym].signal }; + } + /** + * Removes all registered event listeners and resets the registry. + */ + reset() { + this[controllerSym].abort(); + this[controllerSym] = new AbortController(); + } +}; +triggerMapSym, elementCountSym; +var WeakMapRegistry = class { + constructor() { + this[triggerMapSym] = /* @__PURE__ */ new WeakMap(); + this[elementCountSym] = 0; + } + /** + * Sets active triggers for the specified element. + * + * @param element - An HTML element + * @param triggers - The list of active WML trigger events for the specified element + */ + set(element, triggers) { + if (!this[triggerMapSym].has(element)) { + this[elementCountSym]++; + } + this[triggerMapSym].set(element, triggers); + return {}; + } + /** + * Removes all registered event listeners. + */ + reset() { + if (this[elementCountSym] <= 0) + return; + for (const element of document.body.querySelectorAll("*")) { + if (this[elementCountSym] <= 0) + break; + const triggers = this[triggerMapSym].get(element); + if (triggers != null) { + this[elementCountSym]--; + } + for (const trigger of triggers || []) + element.removeEventListener(trigger, onWMLTriggered); + } + this[triggerMapSym] = /* @__PURE__ */ new WeakMap(); + this[elementCountSym] = 0; + } +}; +var triggerRegistry = canAbortListeners() ? new AbortControllerRegistry() : new WeakMapRegistry(); +function addWMLListeners(element) { + const triggerRegExp = /\S+/g; + const triggerAttr = element.getAttribute("wml-trigger") || element.getAttribute("data-wml-trigger") || "click"; + const triggers = []; + let match; + while ((match = triggerRegExp.exec(triggerAttr)) !== null) + triggers.push(match[0]); + const options = triggerRegistry.set(element, triggers); + for (const trigger of triggers) + element.addEventListener(trigger, onWMLTriggered, options); +} +function Enable() { + whenReady(Reload); +} +function Reload() { + triggerRegistry.reset(); + document.body.querySelectorAll("[wml-event], [wml-window], [wml-openurl], [data-wml-event], [data-wml-window], [data-wml-openurl]").forEach(addWMLListeners); +} + +// desktop/compiled/main.js +window.wails = index_exports; +Enable(); +if (true) { + debugLog("Wails Runtime Loaded"); +} + +// desktop/@wailsio/runtime/src/system.ts +var system_exports = {}; +__export(system_exports, { + Capabilities: () => Capabilities, + Environment: () => Environment, + HandlePlatformFileDrop: () => HandlePlatformFileDrop, + IsAMD64: () => IsAMD64, + IsARM: () => IsARM, + IsARM64: () => IsARM64, + IsDarkMode: () => IsDarkMode, + IsDebug: () => IsDebug, + IsLinux: () => IsLinux, + IsMac: () => IsMac, + IsWindows: () => IsWindows, + invoke: () => invoke +}); +var call4 = newRuntimeCaller(objectNames.System); +var SystemIsDarkMode = 0; +var SystemEnvironment = 1; +var ApplicationFilesDroppedWithContext = 100; +var _invoke = function() { + var _a2, _b, _c, _d, _e; + try { + if ((_b = (_a2 = window.chrome) == null ? void 0 : _a2.webview) == null ? void 0 : _b.postMessage) { + return window.chrome.webview.postMessage.bind(window.chrome.webview); + } else if ((_e = (_d = (_c = window.webkit) == null ? void 0 : _c.messageHandlers) == null ? void 0 : _d["external"]) == null ? void 0 : _e.postMessage) { + return window.webkit.messageHandlers["external"].postMessage.bind(window.webkit.messageHandlers["external"]); + } + } catch (e) { + } + console.warn( + "\n%c\u26A0\uFE0F Browser Environment Detected %c\n\n%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode.\nMore information at: https://v3.wails.io/learn/build/#using-a-browser-for-development\n", + "background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;", + "background: transparent;", + "color: #ffffff; font-style: italic; font-weight: bold;" + ); + return null; +}(); +function invoke(msg) { + _invoke == null ? void 0 : _invoke(msg); +} +function IsDarkMode() { + return call4(SystemIsDarkMode); +} +async function Capabilities() { + let response = await fetch("/wails/capabilities"); + if (response.ok) { + return response.json(); + } else { + throw new Error("could not fetch capabilities: " + response.statusText); + } +} +function Environment() { + return call4(SystemEnvironment); +} +function IsWindows() { + return window._wails.environment.OS === "windows"; +} +function IsLinux() { + return window._wails.environment.OS === "linux"; +} +function IsMac() { + return window._wails.environment.OS === "darwin"; +} +function IsAMD64() { + return window._wails.environment.Arch === "amd64"; +} +function IsARM() { + return window._wails.environment.Arch === "arm"; +} +function IsARM64() { + return window._wails.environment.Arch === "arm64"; +} +function IsDebug() { + return Boolean(window._wails.environment.Debug); +} +function HandlePlatformFileDrop(filenames, x, y) { + const element = document.elementFromPoint(x, y); + const elementId = element ? element.id : ""; + const classList = element ? Array.from(element.classList) : []; + const payload = { + filenames, + x, + y, + elementId, + classList + }; + call4(ApplicationFilesDroppedWithContext, payload).then(() => { + console.log("Platform file drop processed and sent to Go."); + }).catch((err) => { + console.error("Error sending platform file drop to Go:", err); + }); +} + +// desktop/@wailsio/runtime/src/contextmenu.ts +window.addEventListener("contextmenu", contextMenuHandler); +var call5 = newRuntimeCaller(objectNames.ContextMenu); +var ContextMenuOpen = 0; +function openContextMenu(id, x, y, data) { + void call5(ContextMenuOpen, { id, x, y, data }); +} +function contextMenuHandler(event) { + const target = eventTarget(event); + const customContextMenu = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu").trim(); + if (customContextMenu) { + event.preventDefault(); + const data = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu-data"); + openContextMenu(customContextMenu, event.clientX, event.clientY, data); + } else { + processDefaultContextMenu(event, target); + } +} +function processDefaultContextMenu(event, target) { + if (IsDebug()) { + return; + } + switch (window.getComputedStyle(target).getPropertyValue("--default-contextmenu").trim()) { + case "show": + return; + case "hide": + event.preventDefault(); + return; + } + if (target.isContentEditable) { + return; + } + const selection = window.getSelection(); + const hasSelection = selection && selection.toString().length > 0; + if (hasSelection) { + for (let i = 0; i < selection.rangeCount; i++) { + const range = selection.getRangeAt(i); + const rects = range.getClientRects(); + for (let j = 0; j < rects.length; j++) { + const rect = rects[j]; + if (document.elementFromPoint(rect.left, rect.top) === target) { + return; + } + } + } + } + if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { + if (hasSelection || !target.readOnly && !target.disabled) { + return; + } + } + event.preventDefault(); +} + +// desktop/@wailsio/runtime/src/flags.ts +var flags_exports = {}; +__export(flags_exports, { + GetFlag: () => GetFlag +}); +function GetFlag(key) { + try { + return window._wails.flags[key]; + } catch (e) { + throw new Error("Unable to retrieve flag '" + key + "': " + e, { cause: e }); + } +} + +// desktop/@wailsio/runtime/src/drag.ts +var canDrag = false; +var dragging = false; +var resizable = false; +var canResize = false; +var resizing = false; +var resizeEdge = ""; +var defaultCursor = "auto"; +var buttons = 0; +var buttonsTracked = canTrackButtons(); +window._wails = window._wails || {}; +window._wails.setResizable = (value) => { + resizable = value; + if (!resizable) { + canResize = resizing = false; + setResize(); + } +}; +window.addEventListener("mousedown", update, { capture: true }); +window.addEventListener("mousemove", update, { capture: true }); +window.addEventListener("mouseup", update, { capture: true }); +for (const ev of ["click", "contextmenu", "dblclick"]) { + window.addEventListener(ev, suppressEvent, { capture: true }); +} +function suppressEvent(event) { + if (dragging || resizing) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } +} +var MouseDown = 0; +var MouseUp = 1; +var MouseMove = 2; +function update(event) { + let eventType, eventButtons = event.buttons; + switch (event.type) { + case "mousedown": + eventType = MouseDown; + if (!buttonsTracked) { + eventButtons = buttons | 1 << event.button; + } + break; + case "mouseup": + eventType = MouseUp; + if (!buttonsTracked) { + eventButtons = buttons & ~(1 << event.button); + } + break; + default: + eventType = MouseMove; + if (!buttonsTracked) { + eventButtons = buttons; + } + break; + } + let released = buttons & ~eventButtons; + let pressed = eventButtons & ~buttons; + buttons = eventButtons; + if (eventType === MouseDown && !(pressed & event.button)) { + released |= 1 << event.button; + pressed |= 1 << event.button; + } + if (eventType !== MouseMove && resizing || dragging && (eventType === MouseDown || event.button !== 0)) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } + if (released & 1) { + primaryUp(event); + } + if (pressed & 1) { + primaryDown(event); + } + if (eventType === MouseMove) { + onMouseMove(event); + } + ; +} +function primaryDown(event) { + canDrag = false; + canResize = false; + if (!IsWindows()) { + if (event.type === "mousedown" && event.button === 0 && event.detail !== 1) { + return; + } + } + if (resizeEdge) { + canResize = true; + return; + } + const target = eventTarget(event); + const style = window.getComputedStyle(target); + canDrag = style.getPropertyValue("--wails-draggable").trim() === "drag" && (event.offsetX - parseFloat(style.paddingLeft) < target.clientWidth && event.offsetY - parseFloat(style.paddingTop) < target.clientHeight); +} +function primaryUp(event) { + canDrag = false; + dragging = false; + canResize = false; + resizing = false; +} +var cursorForEdge = Object.freeze({ + "se-resize": "nwse-resize", + "sw-resize": "nesw-resize", + "nw-resize": "nwse-resize", + "ne-resize": "nesw-resize", + "w-resize": "ew-resize", + "n-resize": "ns-resize", + "s-resize": "ns-resize", + "e-resize": "ew-resize" +}); +function setResize(edge) { + if (edge) { + if (!resizeEdge) { + defaultCursor = document.body.style.cursor; + } + document.body.style.cursor = cursorForEdge[edge]; + } else if (!edge && resizeEdge) { + document.body.style.cursor = defaultCursor; + } + resizeEdge = edge || ""; +} +function onMouseMove(event) { + if (canResize && resizeEdge) { + resizing = true; + invoke("wails:resize:" + resizeEdge); + } else if (canDrag) { + dragging = true; + invoke("wails:drag"); + } + if (dragging || resizing) { + canDrag = canResize = false; + return; + } + if (!resizable || !IsWindows()) { + if (resizeEdge) { + setResize(); + } + return; + } + const resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5; + const resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5; + const cornerExtra = GetFlag("resizeCornerExtra") || 10; + const rightBorder = window.outerWidth - event.clientX < resizeHandleWidth; + const leftBorder = event.clientX < resizeHandleWidth; + const topBorder = event.clientY < resizeHandleHeight; + const bottomBorder = window.outerHeight - event.clientY < resizeHandleHeight; + const rightCorner = window.outerWidth - event.clientX < resizeHandleWidth + cornerExtra; + const leftCorner = event.clientX < resizeHandleWidth + cornerExtra; + const topCorner = event.clientY < resizeHandleHeight + cornerExtra; + const bottomCorner = window.outerHeight - event.clientY < resizeHandleHeight + cornerExtra; + if (!leftCorner && !topCorner && !bottomCorner && !rightCorner) { + setResize(); + } else if (rightCorner && bottomCorner) setResize("se-resize"); + else if (leftCorner && bottomCorner) setResize("sw-resize"); + else if (leftCorner && topCorner) setResize("nw-resize"); + else if (topCorner && rightCorner) setResize("ne-resize"); + else if (leftBorder) setResize("w-resize"); + else if (topBorder) setResize("n-resize"); + else if (bottomBorder) setResize("s-resize"); + else if (rightBorder) setResize("e-resize"); + else setResize(); +} + +// desktop/@wailsio/runtime/src/application.ts +var application_exports = {}; +__export(application_exports, { + Hide: () => Hide, + Quit: () => Quit, + Show: () => Show +}); +var call6 = newRuntimeCaller(objectNames.Application); +var HideMethod2 = 0; +var ShowMethod2 = 1; +var QuitMethod = 2; +function Hide() { + return call6(HideMethod2); +} +function Show() { + return call6(ShowMethod2); +} +function Quit() { + return call6(QuitMethod); +} + +// desktop/@wailsio/runtime/src/calls.ts +var calls_exports = {}; +__export(calls_exports, { + ByID: () => ByID, + ByName: () => ByName, + Call: () => Call, + RuntimeError: () => RuntimeError +}); + +// desktop/@wailsio/runtime/src/callable.ts +var fnToStr = Function.prototype.toString; +var reflectApply = typeof Reflect === "object" && Reflect !== null && Reflect.apply; +var badArrayLike; +var isCallableMarker; +if (typeof reflectApply === "function" && typeof Object.defineProperty === "function") { + try { + badArrayLike = Object.defineProperty({}, "length", { + get: function() { + throw isCallableMarker; + } + }); + isCallableMarker = {}; + reflectApply(function() { + throw 42; + }, null, badArrayLike); + } catch (_) { + if (_ !== isCallableMarker) { + reflectApply = null; + } + } +} else { + reflectApply = null; +} +var constructorRegex = /^\s*class\b/; +var isES6ClassFn = function isES6ClassFunction(value) { + try { + var fnStr = fnToStr.call(value); + return constructorRegex.test(fnStr); + } catch (e) { + return false; + } +}; +var tryFunctionObject = function tryFunctionToStr(value) { + try { + if (isES6ClassFn(value)) { + return false; + } + fnToStr.call(value); + return true; + } catch (e) { + return false; + } +}; +var toStr = Object.prototype.toString; +var objectClass = "[object Object]"; +var fnClass = "[object Function]"; +var genClass = "[object GeneratorFunction]"; +var ddaClass = "[object HTMLAllCollection]"; +var ddaClass2 = "[object HTML document.all class]"; +var ddaClass3 = "[object HTMLCollection]"; +var hasToStringTag = typeof Symbol === "function" && !!Symbol.toStringTag; +var isIE68 = !(0 in [,]); +var isDDA = function isDocumentDotAll() { + return false; +}; +if (typeof document === "object") { + all = document.all; + if (toStr.call(all) === toStr.call(document.all)) { + isDDA = function isDocumentDotAll2(value) { + if ((isIE68 || !value) && (typeof value === "undefined" || typeof value === "object")) { + try { + var str = toStr.call(value); + return (str === ddaClass || str === ddaClass2 || str === ddaClass3 || str === objectClass) && value("") == null; + } catch (e) { + } + } + return false; + }; + } +} +var all; +function isCallableRefApply(value) { + if (isDDA(value)) { + return true; + } + if (!value) { + return false; + } + if (typeof value !== "function" && typeof value !== "object") { + return false; + } + try { + reflectApply(value, null, badArrayLike); + } catch (e) { + if (e !== isCallableMarker) { + return false; + } + } + return !isES6ClassFn(value) && tryFunctionObject(value); +} +function isCallableNoRefApply(value) { + if (isDDA(value)) { + return true; + } + if (!value) { + return false; + } + if (typeof value !== "function" && typeof value !== "object") { + return false; + } + if (hasToStringTag) { + return tryFunctionObject(value); + } + if (isES6ClassFn(value)) { + return false; + } + var strClass = toStr.call(value); + if (strClass !== fnClass && strClass !== genClass && !/^\[object HTML/.test(strClass)) { + return false; + } + return tryFunctionObject(value); +} +var callable_default = reflectApply ? isCallableRefApply : isCallableNoRefApply; + +// desktop/@wailsio/runtime/src/cancellable.ts +var CancelError = class extends Error { + /** + * Constructs a new `CancelError` instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message, options) { + super(message, options); + this.name = "CancelError"; + } +}; +var CancelledRejectionError = class extends Error { + /** + * Constructs a new `CancelledRejectionError` instance. + * @param promise - The promise that caused the error originally. + * @param reason - The rejection reason. + * @param info - An optional informative message specifying the circumstances in which the error was thrown. + * Defaults to the string `"Unhandled rejection in cancelled promise."`. + */ + constructor(promise, reason, info) { + super((info != null ? info : "Unhandled rejection in cancelled promise.") + " Reason: " + errorMessage(reason), { cause: reason }); + this.promise = promise; + this.name = "CancelledRejectionError"; + } +}; +var barrierSym = Symbol("barrier"); +var cancelImplSym = Symbol("cancelImpl"); +var _a; +var species = (_a = Symbol.species) != null ? _a : Symbol("speciesPolyfill"); +var CancellablePromise = class _CancellablePromise extends Promise { + /** + * Creates a new `CancellablePromise`. + * + * @param executor - A callback used to initialize the promise. This callback is passed two arguments: + * a `resolve` callback used to resolve the promise with a value + * or the result of another promise (possibly cancellable), + * and a `reject` callback used to reject the promise with a provided reason or error. + * If the value provided to the `resolve` callback is a thenable _and_ cancellable object + * (it has a `then` _and_ a `cancel` method), + * cancellation requests will be forwarded to that object and the oncancelled will not be invoked anymore. + * If any one of the two callbacks is called _after_ the promise has been cancelled, + * the provided values will be cancelled and resolved as usual, + * but their results will be discarded. + * However, if the resolution process ultimately ends up in a rejection + * that is not due to cancellation, the rejection reason + * will be wrapped in a {@link CancelledRejectionError} + * and bubbled up as an unhandled rejection. + * @param oncancelled - It is the caller's responsibility to ensure that any operation + * started by the executor is properly halted upon cancellation. + * This optional callback can be used to that purpose. + * It will be called _synchronously_ with a cancellation cause + * when cancellation is requested, _after_ the promise has already rejected + * with a {@link CancelError}, but _before_ + * any {@link then}/{@link catch}/{@link finally} callback runs. + * If the callback returns a thenable, the promise returned from {@link cancel} + * will only fulfill after the former has settled. + * Unhandled exceptions or rejections from the callback will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as unhandled rejections. + * If the `resolve` callback is called before cancellation with a cancellable promise, + * cancellation requests on this promise will be diverted to that promise, + * and the original `oncancelled` callback will be discarded. + */ + constructor(executor, oncancelled) { + let resolve; + let reject; + super((res, rej) => { + resolve = res; + reject = rej; + }); + if (this.constructor[species] !== Promise) { + throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property."); + } + let promise = { + promise: this, + resolve, + reject, + get oncancelled() { + return oncancelled != null ? oncancelled : null; + }, + set oncancelled(cb) { + oncancelled = cb != null ? cb : void 0; + } + }; + const state = { + get root() { + return state; + }, + resolving: false, + settled: false + }; + void Object.defineProperties(this, { + [barrierSym]: { + configurable: false, + enumerable: false, + writable: true, + value: null + }, + [cancelImplSym]: { + configurable: false, + enumerable: false, + writable: false, + value: cancellerFor(promise, state) + } + }); + const rejector = rejectorFor(promise, state); + try { + executor(resolverFor(promise, state), rejector); + } catch (err) { + if (state.resolving) { + console.log("Unhandled exception in CancellablePromise executor.", err); + } else { + rejector(err); + } + } + } + /** + * Cancels immediately the execution of the operation associated with this promise. + * The promise rejects with a {@link CancelError} instance as reason, + * with the {@link CancelError#cause} property set to the given argument, if any. + * + * Has no effect if called after the promise has already settled; + * repeated calls in particular are safe, but only the first one + * will set the cancellation cause. + * + * The `CancelError` exception _need not_ be handled explicitly _on the promises that are being cancelled:_ + * cancelling a promise with no attached rejection handler does not trigger an unhandled rejection event. + * Therefore, the following idioms are all equally correct: + * ```ts + * new CancellablePromise((resolve, reject) => { ... }).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).catch(...).cancel(); + * ``` + * Whenever some cancelled promise in a chain rejects with a `CancelError` + * with the same cancellation cause as itself, the error will be discarded silently. + * However, the `CancelError` _will still be delivered_ to all attached rejection handlers + * added by {@link then} and related methods: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * cancellable.then(() => { ... }).catch(console.log); + * cancellable.cancel(); // A CancelError is printed to the console. + * ``` + * If the `CancelError` is not handled downstream by the time it reaches + * a _non-cancelled_ promise, it _will_ trigger an unhandled rejection event, + * just like normal rejections would: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * let chained = cancellable.then(() => { ... }).then(() => { ... }); // No catch... + * cancellable.cancel(); // Unhandled rejection event on chained! + * ``` + * Therefore, it is important to either cancel whole promise chains from their tail, + * as shown in the correct idioms above, or take care of handling errors everywhere. + * + * @returns A cancellable promise that _fulfills_ after the cancel callback (if any) + * and all handlers attached up to the call to cancel have run. + * If the cancel callback returns a thenable, the promise returned by `cancel` + * will also wait for that thenable to settle. + * This enables callers to wait for the cancelled operation to terminate + * without being forced to handle potential errors at the call site. + * ```ts + * cancellable.cancel().then(() => { + * // Cleanup finished, it's safe to do something else. + * }, (err) => { + * // Unreachable: the promise returned from cancel will never reject. + * }); + * ``` + * Note that the returned promise will _not_ handle implicitly any rejection + * that might have occurred already in the cancelled chain. + * It will just track whether registered handlers have been executed or not. + * Therefore, unhandled rejections will never be silently handled by calling cancel. + */ + cancel(cause) { + return new _CancellablePromise((resolve) => { + Promise.all([ + this[cancelImplSym](new CancelError("Promise cancelled.", { cause })), + currentBarrier(this) + ]).then(() => resolve(), () => resolve()); + }); + } + /** + * Binds promise cancellation to the abort event of the given {@link AbortSignal}. + * If the signal has already aborted, the promise will be cancelled immediately. + * When either condition is verified, the cancellation cause will be set + * to the signal's abort reason (see {@link AbortSignal#reason}). + * + * Has no effect if called (or if the signal aborts) _after_ the promise has already settled. + * Only the first signal to abort will set the cancellation cause. + * + * For more details about the cancellation process, + * see {@link cancel} and the `CancellablePromise` constructor. + * + * This method enables `await`ing cancellable promises without having + * to store them for future cancellation, e.g.: + * ```ts + * await longRunningOperation().cancelOn(signal); + * ``` + * instead of: + * ```ts + * let promiseToBeCancelled = longRunningOperation(); + * await promiseToBeCancelled; + * ``` + * + * @returns This promise, for method chaining. + */ + cancelOn(signal) { + if (signal.aborted) { + void this.cancel(signal.reason); + } else { + signal.addEventListener("abort", () => void this.cancel(signal.reason), { capture: true }); + } + return this; + } + /** + * Attaches callbacks for the resolution and/or rejection of the `CancellablePromise`. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A `CancellablePromise` for the completion of whichever callback is executed. + * The returned promise is hooked up to propagate cancellation requests up the chain, but not down: + * + * - if the parent promise is cancelled, the `onrejected` handler will be invoked with a `CancelError` + * and the returned promise _will resolve regularly_ with its result; + * - conversely, if the returned promise is cancelled, _the parent promise is cancelled too;_ + * the `onrejected` handler will still be invoked with the parent's `CancelError`, + * but its result will be discarded + * and the returned promise will reject with a `CancelError` as well. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If either callback returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + */ + then(onfulfilled, onrejected, oncancelled) { + if (!(this instanceof _CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.then called on an invalid object."); + } + if (!callable_default(onfulfilled)) { + onfulfilled = identity; + } + if (!callable_default(onrejected)) { + onrejected = thrower; + } + if (onfulfilled === identity && onrejected == thrower) { + return new _CancellablePromise((resolve) => resolve(this)); + } + const barrier = {}; + this[barrierSym] = barrier; + return new _CancellablePromise((resolve, reject) => { + void super.then( + (value) => { + var _a2; + if (this[barrierSym] === barrier) { + this[barrierSym] = null; + } + (_a2 = barrier.resolve) == null ? void 0 : _a2.call(barrier); + try { + resolve(onfulfilled(value)); + } catch (err) { + reject(err); + } + }, + (reason) => { + var _a2; + if (this[barrierSym] === barrier) { + this[barrierSym] = null; + } + (_a2 = barrier.resolve) == null ? void 0 : _a2.call(barrier); + try { + resolve(onrejected(reason)); + } catch (err) { + reject(err); + } + } + ); + }, async (cause) => { + try { + return oncancelled == null ? void 0 : oncancelled(cause); + } finally { + await this.cancel(cause); + } + }); + } + /** + * Attaches a callback for only the rejection of the Promise. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * It is equivalent to + * ```ts + * cancellablePromise.then(undefined, onrejected, oncancelled); + * ``` + * and the same caveats apply. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onrejected` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + catch(onrejected, oncancelled) { + return this.then(void 0, onrejected, oncancelled); + } + /** + * Attaches a callback that is invoked when the CancellablePromise is settled (fulfilled or rejected). The + * resolved value cannot be accessed or modified from the callback. + * The returned promise will settle in the same state as the original one + * after the provided callback has completed execution, + * unless the callback throws or returns a rejecting promise, + * in which case the returned promise will reject as well. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * Once the parent promise settles, the `onfinally` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * This method is implemented in terms of {@link then} and the same caveats apply. + * It is polyfilled, hence available in every OS/webview version. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onfinally` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + finally(onfinally, oncancelled) { + if (!(this instanceof _CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.finally called on an invalid object."); + } + if (!callable_default(onfinally)) { + return this.then(onfinally, onfinally, oncancelled); + } + return this.then( + (value) => _CancellablePromise.resolve(onfinally()).then(() => value), + (reason) => _CancellablePromise.resolve(onfinally()).then(() => { + throw reason; + }), + oncancelled + ); + } + /** + * We use the `[Symbol.species]` static property, if available, + * to disable the built-in automatic subclassing features from {@link Promise}. + * It is critical for performance reasons that extenders do not override this. + * Once the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing + * is either accepted or retired, this implementation will have to be revised accordingly. + * + * @ignore + * @internal + */ + static get [(barrierSym, cancelImplSym, species)]() { + return Promise; + } + static all(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? _CancellablePromise.resolve(collected) : new _CancellablePromise((resolve, reject) => { + void Promise.all(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static allSettled(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? _CancellablePromise.resolve(collected) : new _CancellablePromise((resolve, reject) => { + void Promise.allSettled(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static any(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? _CancellablePromise.resolve(collected) : new _CancellablePromise((resolve, reject) => { + void Promise.any(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static race(values) { + let collected = Array.from(values); + const promise = new _CancellablePromise((resolve, reject) => { + void Promise.race(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + /** + * Creates a new cancelled CancellablePromise for the provided cause. + * + * @group Static Methods + */ + static cancel(cause) { + const p = new _CancellablePromise(() => { + }); + p.cancel(cause); + return p; + } + /** + * Creates a new CancellablePromise that cancels + * after the specified timeout, with the provided cause. + * + * If the {@link AbortSignal.timeout} factory method is available, + * it is used to base the timeout on _active_ time rather than _elapsed_ time. + * Otherwise, `timeout` falls back to {@link setTimeout}. + * + * @group Static Methods + */ + static timeout(milliseconds, cause) { + const promise = new _CancellablePromise(() => { + }); + if (AbortSignal && typeof AbortSignal === "function" && AbortSignal.timeout && typeof AbortSignal.timeout === "function") { + AbortSignal.timeout(milliseconds).addEventListener("abort", () => void promise.cancel(cause)); + } else { + setTimeout(() => void promise.cancel(cause), milliseconds); + } + return promise; + } + static sleep(milliseconds, value) { + return new _CancellablePromise((resolve) => { + setTimeout(() => resolve(value), milliseconds); + }); + } + /** + * Creates a new rejected CancellablePromise for the provided reason. + * + * @group Static Methods + */ + static reject(reason) { + return new _CancellablePromise((_, reject) => reject(reason)); + } + static resolve(value) { + if (value instanceof _CancellablePromise) { + return value; + } + return new _CancellablePromise((resolve) => resolve(value)); + } + /** + * Creates a new CancellablePromise and returns it in an object, along with its resolve and reject functions + * and a getter/setter for the cancellation callback. + * + * This method is polyfilled, hence available in every OS/webview version. + * + * @group Static Methods + */ + static withResolvers() { + let result = { oncancelled: null }; + result.promise = new _CancellablePromise((resolve, reject) => { + result.resolve = resolve; + result.reject = reject; + }, (cause) => { + var _a2; + (_a2 = result.oncancelled) == null ? void 0 : _a2.call(result, cause); + }); + return result; + } +}; +function cancellerFor(promise, state) { + let cancellationPromise = void 0; + return (reason) => { + if (!state.settled) { + state.settled = true; + state.reason = reason; + promise.reject(reason); + void Promise.prototype.then.call(promise.promise, void 0, (err) => { + if (err !== reason) { + throw err; + } + }); + } + if (!state.reason || !promise.oncancelled) { + return; + } + cancellationPromise = new Promise((resolve) => { + try { + resolve(promise.oncancelled(state.reason.cause)); + } catch (err) { + Promise.reject(new CancelledRejectionError(promise.promise, err, "Unhandled exception in oncancelled callback.")); + } + }).catch((reason2) => { + Promise.reject(new CancelledRejectionError(promise.promise, reason2, "Unhandled rejection in oncancelled callback.")); + }); + promise.oncancelled = null; + return cancellationPromise; + }; +} +function resolverFor(promise, state) { + return (value) => { + if (state.resolving) { + return; + } + state.resolving = true; + if (value === promise.promise) { + if (state.settled) { + return; + } + state.settled = true; + promise.reject(new TypeError("A promise cannot be resolved with itself.")); + return; + } + if (value != null && (typeof value === "object" || typeof value === "function")) { + let then; + try { + then = value.then; + } catch (err) { + state.settled = true; + promise.reject(err); + return; + } + if (callable_default(then)) { + try { + let cancel = value.cancel; + if (callable_default(cancel)) { + const oncancelled = (cause) => { + Reflect.apply(cancel, value, [cause]); + }; + if (state.reason) { + void cancellerFor(__spreadProps(__spreadValues({}, promise), { oncancelled }), state)(state.reason); + } else { + promise.oncancelled = oncancelled; + } + } + } catch (e) { + } + const newState = { + root: state.root, + resolving: false, + get settled() { + return this.root.settled; + }, + set settled(value2) { + this.root.settled = value2; + }, + get reason() { + return this.root.reason; + } + }; + const rejector = rejectorFor(promise, newState); + try { + Reflect.apply(then, value, [resolverFor(promise, newState), rejector]); + } catch (err) { + rejector(err); + } + return; + } + } + if (state.settled) { + return; + } + state.settled = true; + promise.resolve(value); + }; +} +function rejectorFor(promise, state) { + return (reason) => { + if (state.resolving) { + return; + } + state.resolving = true; + if (state.settled) { + try { + if (reason instanceof CancelError && state.reason instanceof CancelError && Object.is(reason.cause, state.reason.cause)) { + return; + } + } catch (e) { + } + void Promise.reject(new CancelledRejectionError(promise.promise, reason)); + } else { + state.settled = true; + promise.reject(reason); + } + }; +} +function cancelAll(parent, values, cause) { + const results = []; + for (const value of values) { + let cancel; + try { + if (!callable_default(value.then)) { + continue; + } + cancel = value.cancel; + if (!callable_default(cancel)) { + continue; + } + } catch (e) { + continue; + } + let result; + try { + result = Reflect.apply(cancel, value, [cause]); + } catch (err) { + Promise.reject(new CancelledRejectionError(parent, err, "Unhandled exception in cancel method.")); + continue; + } + if (!result) { + continue; + } + results.push( + (result instanceof Promise ? result : Promise.resolve(result)).catch((reason) => { + Promise.reject(new CancelledRejectionError(parent, reason, "Unhandled rejection in cancel method.")); + }) + ); + } + return Promise.all(results); +} +function identity(x) { + return x; +} +function thrower(reason) { + throw reason; +} +function errorMessage(err) { + try { + if (err instanceof Error || typeof err !== "object" || err.toString !== Object.prototype.toString) { + return "" + err; + } + } catch (e) { + } + try { + return JSON.stringify(err); + } catch (e) { + } + try { + return Object.prototype.toString.call(err); + } catch (e) { + } + return ""; +} +function currentBarrier(promise) { + var _a2; + let pwr = (_a2 = promise[barrierSym]) != null ? _a2 : {}; + if (!("promise" in pwr)) { + Object.assign(pwr, promiseWithResolvers()); + } + if (promise[barrierSym] == null) { + pwr.resolve(); + promise[barrierSym] = pwr; + } + return pwr.promise; +} +var promiseWithResolvers = Promise.withResolvers; +if (promiseWithResolvers && typeof promiseWithResolvers === "function") { + promiseWithResolvers = promiseWithResolvers.bind(Promise); +} else { + promiseWithResolvers = function() { + let resolve; + let reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; + }; +} + +// desktop/@wailsio/runtime/src/calls.ts +window._wails = window._wails || {}; +window._wails.callResultHandler = resultHandler; +window._wails.callErrorHandler = errorHandler; +var call7 = newRuntimeCaller(objectNames.Call); +var cancelCall = newRuntimeCaller(objectNames.CancelCall); +var callResponses = /* @__PURE__ */ new Map(); +var CallBinding = 0; +var CancelMethod = 0; +var RuntimeError = class extends Error { + /** + * Constructs a new RuntimeError instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message, options) { + super(message, options); + this.name = "RuntimeError"; + } +}; +function resultHandler(id, data, isJSON) { + const resolvers = getAndDeleteResponse2(id); + if (!resolvers) { + return; + } + if (!data) { + resolvers.resolve(void 0); + } else if (!isJSON) { + resolvers.resolve(data); + } else { + try { + resolvers.resolve(JSON.parse(data)); + } catch (err) { + resolvers.reject(new TypeError("could not parse result: " + err.message, { cause: err })); + } + } +} +function errorHandler(id, data, isJSON) { + const resolvers = getAndDeleteResponse2(id); + if (!resolvers) { + return; + } + if (!isJSON) { + resolvers.reject(new Error(data)); + } else { + let error; + try { + error = JSON.parse(data); + } catch (err) { + resolvers.reject(new TypeError("could not parse error: " + err.message, { cause: err })); + return; + } + let options = {}; + if (error.cause) { + options.cause = error.cause; + } + let exception; + switch (error.kind) { + case "ReferenceError": + exception = new ReferenceError(error.message, options); + break; + case "TypeError": + exception = new TypeError(error.message, options); + break; + case "RuntimeError": + exception = new RuntimeError(error.message, options); + break; + default: + exception = new Error(error.message, options); + break; + } + resolvers.reject(exception); + } +} +function getAndDeleteResponse2(id) { + const response = callResponses.get(id); + callResponses.delete(id); + return response; +} +function generateID2() { + let result; + do { + result = nanoid(); + } while (callResponses.has(result)); + return result; +} +function Call(options) { + const id = generateID2(); + const result = CancellablePromise.withResolvers(); + callResponses.set(id, { resolve: result.resolve, reject: result.reject }); + const request = call7(CallBinding, Object.assign({ "call-id": id }, options)); + let running = false; + request.then(() => { + running = true; + }, (err) => { + callResponses.delete(id); + result.reject(err); + }); + const cancel = () => { + callResponses.delete(id); + return cancelCall(CancelMethod, { "call-id": id }).catch((err) => { + console.error("Error while requesting binding call cancellation:", err); + }); + }; + result.oncancelled = () => { + if (running) { + return cancel(); + } else { + return request.then(cancel); + } + }; + return result.promise; +} +function ByName(methodName, ...args) { + return Call({ methodName, args }); +} +function ByID(methodID, ...args) { + return Call({ methodID, args }); +} + +// desktop/@wailsio/runtime/src/clipboard.ts +var clipboard_exports = {}; +__export(clipboard_exports, { + SetText: () => SetText, + Text: () => Text +}); +var call8 = newRuntimeCaller(objectNames.Clipboard); +var ClipboardSetText = 0; +var ClipboardText = 1; +function SetText(text) { + return call8(ClipboardSetText, { text }); +} +function Text() { + return call8(ClipboardText); +} + +// desktop/@wailsio/runtime/src/create.ts +var create_exports = {}; +__export(create_exports, { + Any: () => Any, + Array: () => Array2, + ByteSlice: () => ByteSlice, + Map: () => Map2, + Nullable: () => Nullable, + Struct: () => Struct +}); +function Any(source) { + return source; +} +function ByteSlice(source) { + return source == null ? "" : source; +} +function Array2(element) { + if (element === Any) { + return (source) => source === null ? [] : source; + } + return (source) => { + if (source === null) { + return []; + } + for (let i = 0; i < source.length; i++) { + source[i] = element(source[i]); + } + return source; + }; +} +function Map2(key, value) { + if (value === Any) { + return (source) => source === null ? {} : source; + } + return (source) => { + if (source === null) { + return {}; + } + for (const key2 in source) { + source[key2] = value(source[key2]); + } + return source; + }; +} +function Nullable(element) { + if (element === Any) { + return Any; + } + return (source) => source === null ? null : element(source); +} +function Struct(createField) { + let allAny = true; + for (const name in createField) { + if (createField[name] !== Any) { + allAny = false; + break; + } + } + if (allAny) { + return Any; + } + return (source) => { + for (const name in createField) { + if (name in source) { + source[name] = createField[name](source[name]); + } + } + return source; + }; +} + +// desktop/@wailsio/runtime/src/screens.ts +var screens_exports = {}; +__export(screens_exports, { + GetAll: () => GetAll, + GetCurrent: () => GetCurrent, + GetPrimary: () => GetPrimary +}); +var call9 = newRuntimeCaller(objectNames.Screens); +var getAll = 0; +var getPrimary = 1; +var getCurrent = 2; +function GetAll() { + return call9(getAll); +} +function GetPrimary() { + return call9(getPrimary); +} +function GetCurrent() { + return call9(getCurrent); +} + +// desktop/@wailsio/runtime/src/index.ts +window._wails = window._wails || {}; +window._wails.invoke = invoke; +invoke("wails:runtime:ready"); +export { + application_exports as Application, + browser_exports as Browser, + calls_exports as Call, + CancelError, + CancellablePromise, + CancelledRejectionError, + clipboard_exports as Clipboard, + create_exports as Create, + dialogs_exports as Dialogs, + events_exports as Events, + flags_exports as Flags, + screens_exports as Screens, + system_exports as System, + wml_exports as WML, + window_default as Window +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL2luZGV4LnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy93bWwudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL2Jyb3dzZXIudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL25hbm9pZC50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvcnVudGltZS50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvZGlhbG9ncy50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvZXZlbnRzLnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy9saXN0ZW5lci50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvZXZlbnRfdHlwZXMudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL3V0aWxzLnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy93aW5kb3cudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL2NvbXBpbGVkL21haW4uanMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL3N5c3RlbS50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvY29udGV4dG1lbnUudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL2ZsYWdzLnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy9kcmFnLnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy9hcHBsaWNhdGlvbi50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvY2FsbHMudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL2NhbGxhYmxlLnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy9jYW5jZWxsYWJsZS50cyIsICIuLi8uLi9ydW50aW1lL2Rlc2t0b3AvQHdhaWxzaW8vcnVudGltZS9zcmMvY2xpcGJvYXJkLnRzIiwgIi4uLy4uL3J1bnRpbWUvZGVza3RvcC9Ad2FpbHNpby9ydW50aW1lL3NyYy9jcmVhdGUudHMiLCAiLi4vLi4vcnVudGltZS9kZXNrdG9wL0B3YWlsc2lvL3J1bnRpbWUvc3JjL3NjcmVlbnMudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qXHJcbiBfXHQgICBfX1x0ICBfIF9fXHJcbnwgfFx0IC8gL19fXyBfKF8pIC9fX19fXHJcbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cclxufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXHJcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xyXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXHJcbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcclxuKi9cclxuXHJcbi8vIFNldHVwXHJcbndpbmRvdy5fd2FpbHMgPSB3aW5kb3cuX3dhaWxzIHx8IHt9O1xyXG5cclxuaW1wb3J0IFwiLi9jb250ZXh0bWVudS5qc1wiO1xyXG5pbXBvcnQgXCIuL2RyYWcuanNcIjtcclxuXHJcbi8vIFJlLWV4cG9ydCBwdWJsaWMgQVBJXHJcbmltcG9ydCAqIGFzIEFwcGxpY2F0aW9uIGZyb20gXCIuL2FwcGxpY2F0aW9uLmpzXCI7XHJcbmltcG9ydCAqIGFzIEJyb3dzZXIgZnJvbSBcIi4vYnJvd3Nlci5qc1wiO1xyXG5pbXBvcnQgKiBhcyBDYWxsIGZyb20gXCIuL2NhbGxzLmpzXCI7XHJcbmltcG9ydCAqIGFzIENsaXBib2FyZCBmcm9tIFwiLi9jbGlwYm9hcmQuanNcIjtcclxuaW1wb3J0ICogYXMgQ3JlYXRlIGZyb20gXCIuL2NyZWF0ZS5qc1wiO1xyXG5pbXBvcnQgKiBhcyBEaWFsb2dzIGZyb20gXCIuL2RpYWxvZ3MuanNcIjtcclxuaW1wb3J0ICogYXMgRXZlbnRzIGZyb20gXCIuL2V2ZW50cy5qc1wiO1xyXG5pbXBvcnQgKiBhcyBGbGFncyBmcm9tIFwiLi9mbGFncy5qc1wiO1xyXG5pbXBvcnQgKiBhcyBTY3JlZW5zIGZyb20gXCIuL3NjcmVlbnMuanNcIjtcclxuaW1wb3J0ICogYXMgU3lzdGVtIGZyb20gXCIuL3N5c3RlbS5qc1wiO1xyXG5pbXBvcnQgV2luZG93IGZyb20gXCIuL3dpbmRvdy5qc1wiO1xyXG5pbXBvcnQgKiBhcyBXTUwgZnJvbSBcIi4vd21sLmpzXCI7XHJcblxyXG5leHBvcnQge1xyXG4gICAgQXBwbGljYXRpb24sXHJcbiAgICBCcm93c2VyLFxyXG4gICAgQ2FsbCxcclxuICAgIENsaXBib2FyZCxcclxuICAgIERpYWxvZ3MsXHJcbiAgICBFdmVudHMsXHJcbiAgICBGbGFncyxcclxuICAgIFNjcmVlbnMsXHJcbiAgICBTeXN0ZW0sXHJcbiAgICBXaW5kb3csXHJcbiAgICBXTUxcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBbiBpbnRlcm5hbCB1dGlsaXR5IGNvbnN1bWVkIGJ5IHRoZSBiaW5kaW5nIGdlbmVyYXRvci5cclxuICpcclxuICogQGlnbm9yZVxyXG4gKiBAaW50ZXJuYWxcclxuICovXHJcbmV4cG9ydCB7IENyZWF0ZSB9O1xyXG5cclxuZXhwb3J0ICogZnJvbSBcIi4vY2FuY2VsbGFibGUuanNcIjtcclxuXHJcbi8vIE5vdGlmeSBiYWNrZW5kXHJcbndpbmRvdy5fd2FpbHMuaW52b2tlID0gU3lzdGVtLmludm9rZTtcclxuU3lzdGVtLmludm9rZShcIndhaWxzOnJ1bnRpbWU6cmVhZHlcIik7XHJcbiIsICIvKlxyXG4gXyAgICAgX18gICAgIF8gX19cclxufCB8ICAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBPcGVuVVJMIH0gZnJvbSBcIi4vYnJvd3Nlci5qc1wiO1xyXG5pbXBvcnQgeyBRdWVzdGlvbiB9IGZyb20gXCIuL2RpYWxvZ3MuanNcIjtcclxuaW1wb3J0IHsgRW1pdCwgV2FpbHNFdmVudCB9IGZyb20gXCIuL2V2ZW50cy5qc1wiO1xyXG5pbXBvcnQgeyBjYW5BYm9ydExpc3RlbmVycywgd2hlblJlYWR5IH0gZnJvbSBcIi4vdXRpbHMuanNcIjtcclxuaW1wb3J0IFdpbmRvdyBmcm9tIFwiLi93aW5kb3cuanNcIjtcclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhbiBldmVudCB3aXRoIHRoZSBnaXZlbiBuYW1lIGFuZCBvcHRpb25hbCBkYXRhLlxyXG4gKlxyXG4gKiBAcGFyYW0gZXZlbnROYW1lIC0gLSBUaGUgbmFtZSBvZiB0aGUgZXZlbnQgdG8gc2VuZC5cclxuICogQHBhcmFtIFtkYXRhPW51bGxdIC0gLSBPcHRpb25hbCBkYXRhIHRvIHNlbmQgYWxvbmcgd2l0aCB0aGUgZXZlbnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBzZW5kRXZlbnQoZXZlbnROYW1lOiBzdHJpbmcsIGRhdGE6IGFueSA9IG51bGwpOiB2b2lkIHtcclxuICAgIEVtaXQobmV3IFdhaWxzRXZlbnQoZXZlbnROYW1lLCBkYXRhKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxscyBhIG1ldGhvZCBvbiBhIHNwZWNpZmllZCB3aW5kb3cuXHJcbiAqXHJcbiAqIEBwYXJhbSB3aW5kb3dOYW1lIC0gVGhlIG5hbWUgb2YgdGhlIHdpbmRvdyB0byBjYWxsIHRoZSBtZXRob2Qgb24uXHJcbiAqIEBwYXJhbSBtZXRob2ROYW1lIC0gVGhlIG5hbWUgb2YgdGhlIG1ldGhvZCB0byBjYWxsLlxyXG4gKi9cclxuZnVuY3Rpb24gY2FsbFdpbmRvd01ldGhvZCh3aW5kb3dOYW1lOiBzdHJpbmcsIG1ldGhvZE5hbWU6IHN0cmluZykge1xyXG4gICAgY29uc3QgdGFyZ2V0V2luZG93ID0gV2luZG93LkdldCh3aW5kb3dOYW1lKTtcclxuICAgIGNvbnN0IG1ldGhvZCA9ICh0YXJnZXRXaW5kb3cgYXMgYW55KVttZXRob2ROYW1lXTtcclxuXHJcbiAgICBpZiAodHlwZW9mIG1ldGhvZCAhPT0gXCJmdW5jdGlvblwiKSB7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcihgV2luZG93IG1ldGhvZCAnJHttZXRob2ROYW1lfScgbm90IGZvdW5kYCk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgICAgbWV0aG9kLmNhbGwodGFyZ2V0V2luZG93KTtcclxuICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICBjb25zb2xlLmVycm9yKGBFcnJvciBjYWxsaW5nIHdpbmRvdyBtZXRob2QgJyR7bWV0aG9kTmFtZX0nOiBgLCBlKTtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJlc3BvbmRzIHRvIGEgdHJpZ2dlcmluZyBldmVudCBieSBydW5uaW5nIGFwcHJvcHJpYXRlIFdNTCBhY3Rpb25zIGZvciB0aGUgY3VycmVudCB0YXJnZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBvbldNTFRyaWdnZXJlZChldjogRXZlbnQpOiB2b2lkIHtcclxuICAgIGNvbnN0IGVsZW1lbnQgPSBldi5jdXJyZW50VGFyZ2V0IGFzIEVsZW1lbnQ7XHJcblxyXG4gICAgZnVuY3Rpb24gcnVuRWZmZWN0KGNob2ljZSA9IFwiWWVzXCIpIHtcclxuICAgICAgICBpZiAoY2hvaWNlICE9PSBcIlllc1wiKVxyXG4gICAgICAgICAgICByZXR1cm47XHJcblxyXG4gICAgICAgIGNvbnN0IGV2ZW50VHlwZSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKCd3bWwtZXZlbnQnKSB8fCBlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS13bWwtZXZlbnQnKTtcclxuICAgICAgICBjb25zdCB0YXJnZXRXaW5kb3cgPSBlbGVtZW50LmdldEF0dHJpYnV0ZSgnd21sLXRhcmdldC13aW5kb3cnKSB8fCBlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS13bWwtdGFyZ2V0LXdpbmRvdycpIHx8IFwiXCI7XHJcbiAgICAgICAgY29uc3Qgd2luZG93TWV0aG9kID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ3dtbC13aW5kb3cnKSB8fCBlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS13bWwtd2luZG93Jyk7XHJcbiAgICAgICAgY29uc3QgdXJsID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ3dtbC1vcGVudXJsJykgfHwgZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtd21sLW9wZW51cmwnKTtcclxuXHJcbiAgICAgICAgaWYgKGV2ZW50VHlwZSAhPT0gbnVsbClcclxuICAgICAgICAgICAgc2VuZEV2ZW50KGV2ZW50VHlwZSk7XHJcbiAgICAgICAgaWYgKHdpbmRvd01ldGhvZCAhPT0gbnVsbClcclxuICAgICAgICAgICAgY2FsbFdpbmRvd01ldGhvZCh0YXJnZXRXaW5kb3csIHdpbmRvd01ldGhvZCk7XHJcbiAgICAgICAgaWYgKHVybCAhPT0gbnVsbClcclxuICAgICAgICAgICAgdm9pZCBPcGVuVVJMKHVybCk7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgY29uZmlybSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKCd3bWwtY29uZmlybScpIHx8IGVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLXdtbC1jb25maXJtJyk7XHJcblxyXG4gICAgaWYgKGNvbmZpcm0pIHtcclxuICAgICAgICBRdWVzdGlvbih7XHJcbiAgICAgICAgICAgIFRpdGxlOiBcIkNvbmZpcm1cIixcclxuICAgICAgICAgICAgTWVzc2FnZTogY29uZmlybSxcclxuICAgICAgICAgICAgRGV0YWNoZWQ6IGZhbHNlLFxyXG4gICAgICAgICAgICBCdXR0b25zOiBbXHJcbiAgICAgICAgICAgICAgICB7IExhYmVsOiBcIlllc1wiIH0sXHJcbiAgICAgICAgICAgICAgICB7IExhYmVsOiBcIk5vXCIsIElzRGVmYXVsdDogdHJ1ZSB9XHJcbiAgICAgICAgICAgIF1cclxuICAgICAgICB9KS50aGVuKHJ1bkVmZmVjdCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAgIHJ1bkVmZmVjdCgpO1xyXG4gICAgfVxyXG59XHJcblxyXG4vLyBQcml2YXRlIGZpZWxkIG5hbWVzLlxyXG5jb25zdCBjb250cm9sbGVyU3ltID0gU3ltYm9sKFwiY29udHJvbGxlclwiKTtcclxuY29uc3QgdHJpZ2dlck1hcFN5bSA9IFN5bWJvbChcInRyaWdnZXJNYXBcIik7XHJcbmNvbnN0IGVsZW1lbnRDb3VudFN5bSA9IFN5bWJvbChcImVsZW1lbnRDb3VudFwiKTtcclxuXHJcbi8qKlxyXG4gKiBBYm9ydENvbnRyb2xsZXJSZWdpc3RyeSBkb2VzIG5vdCBhY3R1YWxseSByZW1lbWJlciBhY3RpdmUgZXZlbnQgbGlzdGVuZXJzOiBpbnN0ZWFkXHJcbiAqIGl0IHRpZXMgdGhlbSB0byBhbiBBYm9ydFNpZ25hbCBhbmQgdXNlcyBhbiBBYm9ydENvbnRyb2xsZXIgdG8gcmVtb3ZlIHRoZW0gYWxsIGF0IG9uY2UuXHJcbiAqL1xyXG5jbGFzcyBBYm9ydENvbnRyb2xsZXJSZWdpc3RyeSB7XHJcbiAgICAvLyBQcml2YXRlIGZpZWxkcy5cclxuICAgIFtjb250cm9sbGVyU3ltXTogQWJvcnRDb250cm9sbGVyO1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHRoaXNbY29udHJvbGxlclN5bV0gPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXR1cm5zIGFuIG9wdGlvbnMgb2JqZWN0IGZvciBhZGRFdmVudExpc3RlbmVyIHRoYXQgdGllcyB0aGUgbGlzdGVuZXJcclxuICAgICAqIHRvIHRoZSBBYm9ydFNpZ25hbCBmcm9tIHRoZSBjdXJyZW50IEFib3J0Q29udHJvbGxlci5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gZWxlbWVudCAtIEFuIEhUTUwgZWxlbWVudFxyXG4gICAgICogQHBhcmFtIHRyaWdnZXJzIC0gVGhlIGxpc3Qgb2YgYWN0aXZlIFdNTCB0cmlnZ2VyIGV2ZW50cyBmb3IgdGhlIHNwZWNpZmllZCBlbGVtZW50c1xyXG4gICAgICovXHJcbiAgICBzZXQoZWxlbWVudDogRWxlbWVudCwgdHJpZ2dlcnM6IHN0cmluZ1tdKTogQWRkRXZlbnRMaXN0ZW5lck9wdGlvbnMge1xyXG4gICAgICAgIHJldHVybiB7IHNpZ25hbDogdGhpc1tjb250cm9sbGVyU3ltXS5zaWduYWwgfTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJlbW92ZXMgYWxsIHJlZ2lzdGVyZWQgZXZlbnQgbGlzdGVuZXJzIGFuZCByZXNldHMgdGhlIHJlZ2lzdHJ5LlxyXG4gICAgICovXHJcbiAgICByZXNldCgpOiB2b2lkIHtcclxuICAgICAgICB0aGlzW2NvbnRyb2xsZXJTeW1dLmFib3J0KCk7XHJcbiAgICAgICAgdGhpc1tjb250cm9sbGVyU3ltXSA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFdlYWtNYXBSZWdpc3RyeSBtYXBzIGFjdGl2ZSB0cmlnZ2VyIGV2ZW50cyB0byBlYWNoIERPTSBlbGVtZW50IHRocm91Z2ggYSBXZWFrTWFwLlxyXG4gKiBUaGlzIGVuc3VyZXMgdGhhdCB0aGUgbWFwcGluZyByZW1haW5zIHByaXZhdGUgdG8gdGhpcyBtb2R1bGUsIHdoaWxlIHN0aWxsIGFsbG93aW5nIGdhcmJhZ2VcclxuICogY29sbGVjdGlvbiBvZiB0aGUgaW52b2x2ZWQgZWxlbWVudHMuXHJcbiAqL1xyXG5jbGFzcyBXZWFrTWFwUmVnaXN0cnkge1xyXG4gICAgLyoqIFN0b3JlcyB0aGUgY3VycmVudCBlbGVtZW50LXRvLXRyaWdnZXIgbWFwcGluZy4gKi9cclxuICAgIFt0cmlnZ2VyTWFwU3ltXTogV2Vha01hcDxFbGVtZW50LCBzdHJpbmdbXT47XHJcbiAgICAvKiogQ291bnRzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgd2l0aCBhY3RpdmUgV01MIHRyaWdnZXJzLiAqL1xyXG4gICAgW2VsZW1lbnRDb3VudFN5bV06IG51bWJlcjtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICB0aGlzW3RyaWdnZXJNYXBTeW1dID0gbmV3IFdlYWtNYXAoKTtcclxuICAgICAgICB0aGlzW2VsZW1lbnRDb3VudFN5bV0gPSAwO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogU2V0cyBhY3RpdmUgdHJpZ2dlcnMgZm9yIHRoZSBzcGVjaWZpZWQgZWxlbWVudC5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gZWxlbWVudCAtIEFuIEhUTUwgZWxlbWVudFxyXG4gICAgICogQHBhcmFtIHRyaWdnZXJzIC0gVGhlIGxpc3Qgb2YgYWN0aXZlIFdNTCB0cmlnZ2VyIGV2ZW50cyBmb3IgdGhlIHNwZWNpZmllZCBlbGVtZW50XHJcbiAgICAgKi9cclxuICAgIHNldChlbGVtZW50OiBFbGVtZW50LCB0cmlnZ2Vyczogc3RyaW5nW10pOiBBZGRFdmVudExpc3RlbmVyT3B0aW9ucyB7XHJcbiAgICAgICAgaWYgKCF0aGlzW3RyaWdnZXJNYXBTeW1dLmhhcyhlbGVtZW50KSkgeyB0aGlzW2VsZW1lbnRDb3VudFN5bV0rKzsgfVxyXG4gICAgICAgIHRoaXNbdHJpZ2dlck1hcFN5bV0uc2V0KGVsZW1lbnQsIHRyaWdnZXJzKTtcclxuICAgICAgICByZXR1cm4ge307XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZW1vdmVzIGFsbCByZWdpc3RlcmVkIGV2ZW50IGxpc3RlbmVycy5cclxuICAgICAqL1xyXG4gICAgcmVzZXQoKTogdm9pZCB7XHJcbiAgICAgICAgaWYgKHRoaXNbZWxlbWVudENvdW50U3ltXSA8PSAwKVxyXG4gICAgICAgICAgICByZXR1cm47XHJcblxyXG4gICAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBkb2N1bWVudC5ib2R5LnF1ZXJ5U2VsZWN0b3JBbGwoJyonKSkge1xyXG4gICAgICAgICAgICBpZiAodGhpc1tlbGVtZW50Q291bnRTeW1dIDw9IDApXHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHRyaWdnZXJzID0gdGhpc1t0cmlnZ2VyTWFwU3ltXS5nZXQoZWxlbWVudCk7XHJcbiAgICAgICAgICAgIGlmICh0cmlnZ2VycyAhPSBudWxsKSB7IHRoaXNbZWxlbWVudENvdW50U3ltXS0tOyB9XHJcblxyXG4gICAgICAgICAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdHJpZ2dlcnMgfHwgW10pXHJcbiAgICAgICAgICAgICAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIodHJpZ2dlciwgb25XTUxUcmlnZ2VyZWQpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpc1t0cmlnZ2VyTWFwU3ltXSA9IG5ldyBXZWFrTWFwKCk7XHJcbiAgICAgICAgdGhpc1tlbGVtZW50Q291bnRTeW1dID0gMDtcclxuICAgIH1cclxufVxyXG5cclxuY29uc3QgdHJpZ2dlclJlZ2lzdHJ5ID0gY2FuQWJvcnRMaXN0ZW5lcnMoKSA/IG5ldyBBYm9ydENvbnRyb2xsZXJSZWdpc3RyeSgpIDogbmV3IFdlYWtNYXBSZWdpc3RyeSgpO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgZXZlbnQgbGlzdGVuZXJzIHRvIHRoZSBzcGVjaWZpZWQgZWxlbWVudC5cclxuICovXHJcbmZ1bmN0aW9uIGFkZFdNTExpc3RlbmVycyhlbGVtZW50OiBFbGVtZW50KTogdm9pZCB7XHJcbiAgICBjb25zdCB0cmlnZ2VyUmVnRXhwID0gL1xcUysvZztcclxuICAgIGNvbnN0IHRyaWdnZXJBdHRyID0gKGVsZW1lbnQuZ2V0QXR0cmlidXRlKCd3bWwtdHJpZ2dlcicpIHx8IGVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLXdtbC10cmlnZ2VyJykgfHwgXCJjbGlja1wiKTtcclxuICAgIGNvbnN0IHRyaWdnZXJzOiBzdHJpbmdbXSA9IFtdO1xyXG5cclxuICAgIGxldCBtYXRjaDtcclxuICAgIHdoaWxlICgobWF0Y2ggPSB0cmlnZ2VyUmVnRXhwLmV4ZWModHJpZ2dlckF0dHIpKSAhPT0gbnVsbClcclxuICAgICAgICB0cmlnZ2Vycy5wdXNoKG1hdGNoWzBdKTtcclxuXHJcbiAgICBjb25zdCBvcHRpb25zID0gdHJpZ2dlclJlZ2lzdHJ5LnNldChlbGVtZW50LCB0cmlnZ2Vycyk7XHJcbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdHJpZ2dlcnMpXHJcbiAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRyaWdnZXIsIG9uV01MVHJpZ2dlcmVkLCBvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNjaGVkdWxlcyBhbiBhdXRvbWF0aWMgcmVsb2FkIG9mIFdNTCB0byBiZSBwZXJmb3JtZWQgYXMgc29vbiBhcyB0aGUgZG9jdW1lbnQgaXMgZnVsbHkgbG9hZGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIEVuYWJsZSgpOiB2b2lkIHtcclxuICAgIHdoZW5SZWFkeShSZWxvYWQpO1xyXG59XHJcblxyXG4vKipcclxuICogUmVsb2FkcyB0aGUgV01MIHBhZ2UgYnkgYWRkaW5nIG5lY2Vzc2FyeSBldmVudCBsaXN0ZW5lcnMgYW5kIGJyb3dzZXIgbGlzdGVuZXJzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIFJlbG9hZCgpOiB2b2lkIHtcclxuICAgIHRyaWdnZXJSZWdpc3RyeS5yZXNldCgpO1xyXG4gICAgZG9jdW1lbnQuYm9keS5xdWVyeVNlbGVjdG9yQWxsKCdbd21sLWV2ZW50XSwgW3dtbC13aW5kb3ddLCBbd21sLW9wZW51cmxdLCBbZGF0YS13bWwtZXZlbnRdLCBbZGF0YS13bWwtd2luZG93XSwgW2RhdGEtd21sLW9wZW51cmxdJykuZm9yRWFjaChhZGRXTUxMaXN0ZW5lcnMpO1xyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBuZXdSdW50aW1lQ2FsbGVyLCBvYmplY3ROYW1lcyB9IGZyb20gXCIuL3J1bnRpbWUuanNcIjtcclxuXHJcbmNvbnN0IGNhbGwgPSBuZXdSdW50aW1lQ2FsbGVyKG9iamVjdE5hbWVzLkJyb3dzZXIpO1xyXG5cclxuY29uc3QgQnJvd3Nlck9wZW5VUkwgPSAwO1xyXG5cclxuLyoqXHJcbiAqIE9wZW4gYSBicm93c2VyIHdpbmRvdyB0byB0aGUgZ2l2ZW4gVVJMLlxyXG4gKlxyXG4gKiBAcGFyYW0gdXJsIC0gVGhlIFVSTCB0byBvcGVuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gT3BlblVSTCh1cmw6IHN0cmluZyB8IFVSTCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgcmV0dXJuIGNhbGwoQnJvd3Nlck9wZW5VUkwsIHt1cmw6IHVybC50b1N0cmluZygpfSk7XHJcbn1cclxuIiwgIi8vIFNvdXJjZTogaHR0cHM6Ly9naXRodWIuY29tL2FpL25hbm9pZFxyXG5cclxuLy8gVGhlIE1JVCBMaWNlbnNlIChNSVQpXHJcbi8vXHJcbi8vIENvcHlyaWdodCAyMDE3IEFuZHJleSBTaXRuaWsgPGFuZHJleUBzaXRuaWsucnU+XHJcbi8vXHJcbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkgb2ZcclxuLy8gdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG8gZGVhbCBpblxyXG4vLyB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvXHJcbi8vIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mXHJcbi8vIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbyxcclxuLy8gICAgIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOlxyXG4vL1xyXG4vLyAgICAgVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW4gYWxsXHJcbi8vIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXHJcbi8vXHJcbi8vICAgICBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SXHJcbi8vIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTXHJcbi8vIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUlxyXG4vLyBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVJcclxuLy8gSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU5cclxuLy8gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS5cclxuXHJcbi8vIFRoaXMgYWxwaGFiZXQgdXNlcyBgQS1aYS16MC05Xy1gIHN5bWJvbHMuXHJcbi8vIFRoZSBvcmRlciBvZiBjaGFyYWN0ZXJzIGlzIG9wdGltaXplZCBmb3IgYmV0dGVyIGd6aXAgYW5kIGJyb3RsaSBjb21wcmVzc2lvbi5cclxuLy8gUmVmZXJlbmNlcyB0byB0aGUgc2FtZSBmaWxlICh3b3JrcyBib3RoIGZvciBnemlwIGFuZCBicm90bGkpOlxyXG4vLyBgJ3VzZWAsIGBhbmRvbWAsIGFuZCBgcmljdCdgXHJcbi8vIFJlZmVyZW5jZXMgdG8gdGhlIGJyb3RsaSBkZWZhdWx0IGRpY3Rpb25hcnk6XHJcbi8vIGAtMjZUYCwgYDE5ODNgLCBgNDBweGAsIGA3NXB4YCwgYGJ1c2hgLCBgamFja2AsIGBtaW5kYCwgYHZlcnlgLCBhbmQgYHdvbGZgXHJcbmNvbnN0IHVybEFscGhhYmV0ID1cclxuICAgICd1c2VhbmRvbS0yNlQxOTgzNDBQWDc1cHhKQUNLVkVSWU1JTkRCVVNIV09MRl9HUVpiZmdoamtscXZ3eXpyaWN0J1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIG5hbm9pZChzaXplOiBudW1iZXIgPSAyMSk6IHN0cmluZyB7XHJcbiAgICBsZXQgaWQgPSAnJ1xyXG4gICAgLy8gQSBjb21wYWN0IGFsdGVybmF0aXZlIGZvciBgZm9yICh2YXIgaSA9IDA7IGkgPCBzdGVwOyBpKyspYC5cclxuICAgIGxldCBpID0gc2l6ZSB8IDBcclxuICAgIHdoaWxlIChpLS0pIHtcclxuICAgICAgICAvLyBgfCAwYCBpcyBtb3JlIGNvbXBhY3QgYW5kIGZhc3RlciB0aGFuIGBNYXRoLmZsb29yKClgLlxyXG4gICAgICAgIGlkICs9IHVybEFscGhhYmV0WyhNYXRoLnJhbmRvbSgpICogNjQpIHwgMF1cclxuICAgIH1cclxuICAgIHJldHVybiBpZFxyXG59XHJcbiIsICIvKlxyXG4gXyAgICAgX18gICAgIF8gX19cclxufCB8ICAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBuYW5vaWQgfSBmcm9tICcuL25hbm9pZC5qcyc7XHJcblxyXG5jb25zdCBydW50aW1lVVJMID0gd2luZG93LmxvY2F0aW9uLm9yaWdpbiArIFwiL3dhaWxzL3J1bnRpbWVcIjtcclxuXHJcbi8vIE9iamVjdCBOYW1lc1xyXG5leHBvcnQgY29uc3Qgb2JqZWN0TmFtZXMgPSBPYmplY3QuZnJlZXplKHtcclxuICAgIENhbGw6IDAsXHJcbiAgICBDbGlwYm9hcmQ6IDEsXHJcbiAgICBBcHBsaWNhdGlvbjogMixcclxuICAgIEV2ZW50czogMyxcclxuICAgIENvbnRleHRNZW51OiA0LFxyXG4gICAgRGlhbG9nOiA1LFxyXG4gICAgV2luZG93OiA2LFxyXG4gICAgU2NyZWVuczogNyxcclxuICAgIFN5c3RlbTogOCxcclxuICAgIEJyb3dzZXI6IDksXHJcbiAgICBDYW5jZWxDYWxsOiAxMCxcclxufSk7XHJcbmV4cG9ydCBsZXQgY2xpZW50SWQgPSBuYW5vaWQoKTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgbmV3IHJ1bnRpbWUgY2FsbGVyIHdpdGggc3BlY2lmaWVkIElELlxyXG4gKlxyXG4gKiBAcGFyYW0gb2JqZWN0IC0gVGhlIG9iamVjdCB0byBpbnZva2UgdGhlIG1ldGhvZCBvbi5cclxuICogQHBhcmFtIHdpbmRvd05hbWUgLSBUaGUgbmFtZSBvZiB0aGUgd2luZG93LlxyXG4gKiBAcmV0dXJuIFRoZSBuZXcgcnVudGltZSBjYWxsZXIgZnVuY3Rpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbmV3UnVudGltZUNhbGxlcihvYmplY3Q6IG51bWJlciwgd2luZG93TmFtZTogc3RyaW5nID0gJycpIHtcclxuICAgIHJldHVybiBmdW5jdGlvbiAobWV0aG9kOiBudW1iZXIsIGFyZ3M6IGFueSA9IG51bGwpIHtcclxuICAgICAgICByZXR1cm4gcnVudGltZUNhbGxXaXRoSUQob2JqZWN0LCBtZXRob2QsIHdpbmRvd05hbWUsIGFyZ3MpO1xyXG4gICAgfTtcclxufVxyXG5cclxuYXN5bmMgZnVuY3Rpb24gcnVudGltZUNhbGxXaXRoSUQob2JqZWN0SUQ6IG51bWJlciwgbWV0aG9kOiBudW1iZXIsIHdpbmRvd05hbWU6IHN0cmluZywgYXJnczogYW55KTogUHJvbWlzZTxhbnk+IHtcclxuICAgIGxldCB1cmwgPSBuZXcgVVJMKHJ1bnRpbWVVUkwpO1xyXG4gICAgdXJsLnNlYXJjaFBhcmFtcy5hcHBlbmQoXCJvYmplY3RcIiwgb2JqZWN0SUQudG9TdHJpbmcoKSk7XHJcbiAgICB1cmwuc2VhcmNoUGFyYW1zLmFwcGVuZChcIm1ldGhvZFwiLCBtZXRob2QudG9TdHJpbmcoKSk7XHJcbiAgICBpZiAoYXJncykgeyB1cmwuc2VhcmNoUGFyYW1zLmFwcGVuZChcImFyZ3NcIiwgSlNPTi5zdHJpbmdpZnkoYXJncykpOyB9XHJcblxyXG4gICAgbGV0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XHJcbiAgICAgICAgW1wieC13YWlscy1jbGllbnQtaWRcIl06IGNsaWVudElkXHJcbiAgICB9XHJcbiAgICBpZiAod2luZG93TmFtZSkge1xyXG4gICAgICAgIGhlYWRlcnNbXCJ4LXdhaWxzLXdpbmRvdy1uYW1lXCJdID0gd2luZG93TmFtZTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh1cmwsIHsgaGVhZGVycyB9KTtcclxuICAgIGlmICghcmVzcG9uc2Uub2spIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYXdhaXQgcmVzcG9uc2UudGV4dCgpKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoKHJlc3BvbnNlLmhlYWRlcnMuZ2V0KFwiQ29udGVudC1UeXBlXCIpPy5pbmRleE9mKFwiYXBwbGljYXRpb24vanNvblwiKSA/PyAtMSkgIT09IC0xKSB7XHJcbiAgICAgICAgcmV0dXJuIHJlc3BvbnNlLmpzb24oKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnRleHQoKTtcclxuICAgIH1cclxufVxyXG4iLCAiLypcclxuIF9cdCAgIF9fXHQgIF8gX19cclxufCB8XHQgLyAvX19fIF8oXykgL19fX19cclxufCB8IC98IC8gLyBfXyBgLyAvIC8gX19fL1xyXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcclxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXHJcblRoZSBlbGVjdHJvbiBhbHRlcm5hdGl2ZSBmb3IgR29cclxuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxyXG4qL1xyXG5cclxuaW1wb3J0IHtuZXdSdW50aW1lQ2FsbGVyLCBvYmplY3ROYW1lc30gZnJvbSBcIi4vcnVudGltZS5qc1wiO1xyXG5pbXBvcnQgeyBuYW5vaWQgfSBmcm9tICcuL25hbm9pZC5qcyc7XHJcblxyXG4vLyBzZXR1cFxyXG53aW5kb3cuX3dhaWxzID0gd2luZG93Ll93YWlscyB8fCB7fTtcclxud2luZG93Ll93YWlscy5kaWFsb2dFcnJvckNhbGxiYWNrID0gZGlhbG9nRXJyb3JDYWxsYmFjaztcclxud2luZG93Ll93YWlscy5kaWFsb2dSZXN1bHRDYWxsYmFjayA9IGRpYWxvZ1Jlc3VsdENhbGxiYWNrO1xyXG5cclxudHlwZSBQcm9taXNlUmVzb2x2ZXJzID0gT21pdDxQcm9taXNlV2l0aFJlc29sdmVyczxhbnk+LCBcInByb21pc2VcIj47XHJcblxyXG5jb25zdCBjYWxsID0gbmV3UnVudGltZUNhbGxlcihvYmplY3ROYW1lcy5EaWFsb2cpO1xyXG5jb25zdCBkaWFsb2dSZXNwb25zZXMgPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZVJlc29sdmVycz4oKTtcclxuXHJcbi8vIERlZmluZSBjb25zdGFudHMgZnJvbSB0aGUgYG1ldGhvZHNgIG9iamVjdCBpbiBUaXRsZSBDYXNlXHJcbmNvbnN0IERpYWxvZ0luZm8gPSAwO1xyXG5jb25zdCBEaWFsb2dXYXJuaW5nID0gMTtcclxuY29uc3QgRGlhbG9nRXJyb3IgPSAyO1xyXG5jb25zdCBEaWFsb2dRdWVzdGlvbiA9IDM7XHJcbmNvbnN0IERpYWxvZ09wZW5GaWxlID0gNDtcclxuY29uc3QgRGlhbG9nU2F2ZUZpbGUgPSA1O1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBPcGVuRmlsZURpYWxvZ09wdGlvbnMge1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBkaXJlY3RvcmllcyBjYW4gYmUgY2hvc2VuLiAqL1xyXG4gICAgQ2FuQ2hvb3NlRGlyZWN0b3JpZXM/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBmaWxlcyBjYW4gYmUgY2hvc2VuLiAqL1xyXG4gICAgQ2FuQ2hvb3NlRmlsZXM/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBkaXJlY3RvcmllcyBjYW4gYmUgY3JlYXRlZC4gKi9cclxuICAgIENhbkNyZWF0ZURpcmVjdG9yaWVzPzogYm9vbGVhbjtcclxuICAgIC8qKiBJbmRpY2F0ZXMgaWYgaGlkZGVuIGZpbGVzIHNob3VsZCBiZSBzaG93bi4gKi9cclxuICAgIFNob3dIaWRkZW5GaWxlcz86IGJvb2xlYW47XHJcbiAgICAvKiogSW5kaWNhdGVzIGlmIGFsaWFzZXMgc2hvdWxkIGJlIHJlc29sdmVkLiAqL1xyXG4gICAgUmVzb2x2ZXNBbGlhc2VzPzogYm9vbGVhbjtcclxuICAgIC8qKiBJbmRpY2F0ZXMgaWYgbXVsdGlwbGUgc2VsZWN0aW9uIGlzIGFsbG93ZWQuICovXHJcbiAgICBBbGxvd3NNdWx0aXBsZVNlbGVjdGlvbj86IGJvb2xlYW47XHJcbiAgICAvKiogSW5kaWNhdGVzIGlmIHRoZSBleHRlbnNpb24gc2hvdWxkIGJlIGhpZGRlbi4gKi9cclxuICAgIEhpZGVFeHRlbnNpb24/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBoaWRkZW4gZXh0ZW5zaW9ucyBjYW4gYmUgc2VsZWN0ZWQuICovXHJcbiAgICBDYW5TZWxlY3RIaWRkZW5FeHRlbnNpb24/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBmaWxlIHBhY2thZ2VzIHNob3VsZCBiZSB0cmVhdGVkIGFzIGRpcmVjdG9yaWVzLiAqL1xyXG4gICAgVHJlYXRzRmlsZVBhY2thZ2VzQXNEaXJlY3Rvcmllcz86IGJvb2xlYW47XHJcbiAgICAvKiogSW5kaWNhdGVzIGlmIG90aGVyIGZpbGUgdHlwZXMgYXJlIGFsbG93ZWQuICovXHJcbiAgICBBbGxvd3NPdGhlckZpbGV0eXBlcz86IGJvb2xlYW47XHJcbiAgICAvKiogQXJyYXkgb2YgZmlsZSBmaWx0ZXJzLiAqL1xyXG4gICAgRmlsdGVycz86IEZpbGVGaWx0ZXJbXTtcclxuICAgIC8qKiBUaXRsZSBvZiB0aGUgZGlhbG9nLiAqL1xyXG4gICAgVGl0bGU/OiBzdHJpbmc7XHJcbiAgICAvKiogTWVzc2FnZSB0byBzaG93IGluIHRoZSBkaWFsb2cuICovXHJcbiAgICBNZXNzYWdlPzogc3RyaW5nO1xyXG4gICAgLyoqIFRleHQgdG8gZGlzcGxheSBvbiB0aGUgYnV0dG9uLiAqL1xyXG4gICAgQnV0dG9uVGV4dD86IHN0cmluZztcclxuICAgIC8qKiBEaXJlY3RvcnkgdG8gb3BlbiBpbiB0aGUgZGlhbG9nLiAqL1xyXG4gICAgRGlyZWN0b3J5Pzogc3RyaW5nO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiB0aGUgZGlhbG9nIHNob3VsZCBhcHBlYXIgZGV0YWNoZWQgZnJvbSB0aGUgbWFpbiB3aW5kb3cuICovXHJcbiAgICBEZXRhY2hlZD86IGJvb2xlYW47XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgU2F2ZUZpbGVEaWFsb2dPcHRpb25zIHtcclxuICAgIC8qKiBEZWZhdWx0IGZpbGVuYW1lIHRvIHVzZSBpbiB0aGUgZGlhbG9nLiAqL1xyXG4gICAgRmlsZW5hbWU/OiBzdHJpbmc7XHJcbiAgICAvKiogSW5kaWNhdGVzIGlmIGRpcmVjdG9yaWVzIGNhbiBiZSBjaG9zZW4uICovXHJcbiAgICBDYW5DaG9vc2VEaXJlY3Rvcmllcz86IGJvb2xlYW47XHJcbiAgICAvKiogSW5kaWNhdGVzIGlmIGZpbGVzIGNhbiBiZSBjaG9zZW4uICovXHJcbiAgICBDYW5DaG9vc2VGaWxlcz86IGJvb2xlYW47XHJcbiAgICAvKiogSW5kaWNhdGVzIGlmIGRpcmVjdG9yaWVzIGNhbiBiZSBjcmVhdGVkLiAqL1xyXG4gICAgQ2FuQ3JlYXRlRGlyZWN0b3JpZXM/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBoaWRkZW4gZmlsZXMgc2hvdWxkIGJlIHNob3duLiAqL1xyXG4gICAgU2hvd0hpZGRlbkZpbGVzPzogYm9vbGVhbjtcclxuICAgIC8qKiBJbmRpY2F0ZXMgaWYgYWxpYXNlcyBzaG91bGQgYmUgcmVzb2x2ZWQuICovXHJcbiAgICBSZXNvbHZlc0FsaWFzZXM/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiB0aGUgZXh0ZW5zaW9uIHNob3VsZCBiZSBoaWRkZW4uICovXHJcbiAgICBIaWRlRXh0ZW5zaW9uPzogYm9vbGVhbjtcclxuICAgIC8qKiBJbmRpY2F0ZXMgaWYgaGlkZGVuIGV4dGVuc2lvbnMgY2FuIGJlIHNlbGVjdGVkLiAqL1xyXG4gICAgQ2FuU2VsZWN0SGlkZGVuRXh0ZW5zaW9uPzogYm9vbGVhbjtcclxuICAgIC8qKiBJbmRpY2F0ZXMgaWYgZmlsZSBwYWNrYWdlcyBzaG91bGQgYmUgdHJlYXRlZCBhcyBkaXJlY3Rvcmllcy4gKi9cclxuICAgIFRyZWF0c0ZpbGVQYWNrYWdlc0FzRGlyZWN0b3JpZXM/OiBib29sZWFuO1xyXG4gICAgLyoqIEluZGljYXRlcyBpZiBvdGhlciBmaWxlIHR5cGVzIGFyZSBhbGxvd2VkLiAqL1xyXG4gICAgQWxsb3dzT3RoZXJGaWxldHlwZXM/OiBib29sZWFuO1xyXG4gICAgLyoqIEFycmF5IG9mIGZpbGUgZmlsdGVycy4gKi9cclxuICAgIEZpbHRlcnM/OiBGaWxlRmlsdGVyW107XHJcbiAgICAvKiogVGl0bGUgb2YgdGhlIGRpYWxvZy4gKi9cclxuICAgIFRpdGxlPzogc3RyaW5nO1xyXG4gICAgLyoqIE1lc3NhZ2UgdG8gc2hvdyBpbiB0aGUgZGlhbG9nLiAqL1xyXG4gICAgTWVzc2FnZT86IHN0cmluZztcclxuICAgIC8qKiBUZXh0IHRvIGRpc3BsYXkgb24gdGhlIGJ1dHRvbi4gKi9cclxuICAgIEJ1dHRvblRleHQ/OiBzdHJpbmc7XHJcbiAgICAvKiogRGlyZWN0b3J5IHRvIG9wZW4gaW4gdGhlIGRpYWxvZy4gKi9cclxuICAgIERpcmVjdG9yeT86IHN0cmluZztcclxuICAgIC8qKiBJbmRpY2F0ZXMgaWYgdGhlIGRpYWxvZyBzaG91bGQgYXBwZWFyIGRldGFjaGVkIGZyb20gdGhlIG1haW4gd2luZG93LiAqL1xyXG4gICAgRGV0YWNoZWQ/OiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VEaWFsb2dPcHRpb25zIHtcclxuICAgIC8qKiBUaGUgdGl0bGUgb2YgdGhlIGRpYWxvZyB3aW5kb3cuICovXHJcbiAgICBUaXRsZT86IHN0cmluZztcclxuICAgIC8qKiBUaGUgbWFpbiBtZXNzYWdlIHRvIHNob3cgaW4gdGhlIGRpYWxvZy4gKi9cclxuICAgIE1lc3NhZ2U/OiBzdHJpbmc7XHJcbiAgICAvKiogQXJyYXkgb2YgYnV0dG9uIG9wdGlvbnMgdG8gc2hvdyBpbiB0aGUgZGlhbG9nLiAqL1xyXG4gICAgQnV0dG9ucz86IEJ1dHRvbltdO1xyXG4gICAgLyoqIFRydWUgaWYgdGhlIGRpYWxvZyBzaG91bGQgYXBwZWFyIGRldGFjaGVkIGZyb20gdGhlIG1haW4gd2luZG93IChpZiBhcHBsaWNhYmxlKS4gKi9cclxuICAgIERldGFjaGVkPzogYm9vbGVhbjtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBCdXR0b24ge1xyXG4gICAgLyoqIFRleHQgdGhhdCBhcHBlYXJzIHdpdGhpbiB0aGUgYnV0dG9uLiAqL1xyXG4gICAgTGFiZWw/OiBzdHJpbmc7XHJcbiAgICAvKiogVHJ1ZSBpZiB0aGUgYnV0dG9uIHNob3VsZCBjYW5jZWwgYW4gb3BlcmF0aW9uIHdoZW4gY2xpY2tlZC4gKi9cclxuICAgIElzQ2FuY2VsPzogYm9vbGVhbjtcclxuICAgIC8qKiBUcnVlIGlmIHRoZSBidXR0b24gc2hvdWxkIGJlIHRoZSBkZWZhdWx0IGFjdGlvbiB3aGVuIHRoZSB1c2VyIHByZXNzZXMgZW50ZXIuICovXHJcbiAgICBJc0RlZmF1bHQ/OiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVGaWx0ZXIge1xyXG4gICAgLyoqIERpc3BsYXkgbmFtZSBmb3IgdGhlIGZpbHRlciwgaXQgY291bGQgYmUgXCJUZXh0IEZpbGVzXCIsIFwiSW1hZ2VzXCIgZXRjLiAqL1xyXG4gICAgRGlzcGxheU5hbWU/OiBzdHJpbmc7XHJcbiAgICAvKiogUGF0dGVybiB0byBtYXRjaCBmb3IgdGhlIGZpbHRlciwgZS5nLiBcIioudHh0OyoubWRcIiBmb3IgdGV4dCBtYXJrZG93biBmaWxlcy4gKi9cclxuICAgIFBhdHRlcm4/OiBzdHJpbmc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSByZXN1bHQgb2YgYSBkaWFsb2cgcmVxdWVzdC5cclxuICpcclxuICogQHBhcmFtIGlkIC0gVGhlIGlkIG9mIHRoZSByZXF1ZXN0IHRvIGhhbmRsZSB0aGUgcmVzdWx0IGZvci5cclxuICogQHBhcmFtIGRhdGEgLSBUaGUgcmVzdWx0IGRhdGEgb2YgdGhlIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSBpc0pTT04gLSBJbmRpY2F0ZXMgd2hldGhlciB0aGUgZGF0YSBpcyBKU09OIG9yIG5vdC5cclxuICovXHJcbmZ1bmN0aW9uIGRpYWxvZ1Jlc3VsdENhbGxiYWNrKGlkOiBzdHJpbmcsIGRhdGE6IHN0cmluZywgaXNKU09OOiBib29sZWFuKTogdm9pZCB7XHJcbiAgICBsZXQgcmVzb2x2ZXJzID0gZ2V0QW5kRGVsZXRlUmVzcG9uc2UoaWQpO1xyXG4gICAgaWYgKCFyZXNvbHZlcnMpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGlzSlNPTikge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc29sdmVycy5yZXNvbHZlKEpTT04ucGFyc2UoZGF0YSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XHJcbiAgICAgICAgICAgIHJlc29sdmVycy5yZWplY3QobmV3IFR5cGVFcnJvcihcImNvdWxkIG5vdCBwYXJzZSByZXN1bHQ6IFwiICsgZXJyLm1lc3NhZ2UsIHsgY2F1c2U6IGVyciB9KSk7XHJcbiAgICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICByZXNvbHZlcnMucmVzb2x2ZShkYXRhKTtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGVycm9yIGZyb20gYSBkaWFsb2cgcmVxdWVzdC5cclxuICpcclxuICogQHBhcmFtIGlkIC0gVGhlIGlkIG9mIHRoZSBwcm9taXNlIGhhbmRsZXIuXHJcbiAqIEBwYXJhbSBtZXNzYWdlIC0gQW4gZXJyb3IgbWVzc2FnZS5cclxuICovXHJcbmZ1bmN0aW9uIGRpYWxvZ0Vycm9yQ2FsbGJhY2soaWQ6IHN0cmluZywgbWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XHJcbiAgICBnZXRBbmREZWxldGVSZXNwb25zZShpZCk/LnJlamVjdChuZXcgd2luZG93LkVycm9yKG1lc3NhZ2UpKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmVtb3ZlcyB0aGUgcmVzcG9uc2UgYXNzb2NpYXRlZCB3aXRoIHRoZSBnaXZlbiBJRCBmcm9tIHRoZSBkaWFsb2dSZXNwb25zZXMgbWFwLlxyXG4gKlxyXG4gKiBAcGFyYW0gaWQgLSBUaGUgSUQgb2YgdGhlIHJlc3BvbnNlIHRvIGJlIHJldHJpZXZlZCBhbmQgcmVtb3ZlZC5cclxuICogQHJldHVybnMgVGhlIHJlc3BvbnNlIG9iamVjdCBhc3NvY2lhdGVkIHdpdGggdGhlIGdpdmVuIElELCBpZiBhbnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBnZXRBbmREZWxldGVSZXNwb25zZShpZDogc3RyaW5nKTogUHJvbWlzZVJlc29sdmVycyB8IHVuZGVmaW5lZCB7XHJcbiAgICBjb25zdCByZXNwb25zZSA9IGRpYWxvZ1Jlc3BvbnNlcy5nZXQoaWQpO1xyXG4gICAgZGlhbG9nUmVzcG9uc2VzLmRlbGV0ZShpZCk7XHJcbiAgICByZXR1cm4gcmVzcG9uc2U7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZW5lcmF0ZXMgYSB1bmlxdWUgSUQgdXNpbmcgdGhlIG5hbm9pZCBsaWJyYXJ5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyBBIHVuaXF1ZSBJRCB0aGF0IGRvZXMgbm90IGV4aXN0IGluIHRoZSBkaWFsb2dSZXNwb25zZXMgc2V0LlxyXG4gKi9cclxuZnVuY3Rpb24gZ2VuZXJhdGVJRCgpOiBzdHJpbmcge1xyXG4gICAgbGV0IHJlc3VsdDtcclxuICAgIGRvIHtcclxuICAgICAgICByZXN1bHQgPSBuYW5vaWQoKTtcclxuICAgIH0gd2hpbGUgKGRpYWxvZ1Jlc3BvbnNlcy5oYXMocmVzdWx0KSk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG59XHJcblxyXG4vKipcclxuICogUHJlc2VudHMgYSBkaWFsb2cgb2Ygc3BlY2lmaWVkIHR5cGUgd2l0aCB0aGUgZ2l2ZW4gb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHR5cGUgLSBEaWFsb2cgdHlwZS5cclxuICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgZGlhbG9nLlxyXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHJlc3VsdCBvZiBkaWFsb2cuXHJcbiAqL1xyXG5mdW5jdGlvbiBkaWFsb2codHlwZTogbnVtYmVyLCBvcHRpb25zOiBNZXNzYWdlRGlhbG9nT3B0aW9ucyB8IE9wZW5GaWxlRGlhbG9nT3B0aW9ucyB8IFNhdmVGaWxlRGlhbG9nT3B0aW9ucyA9IHt9KTogUHJvbWlzZTxhbnk+IHtcclxuICAgIGNvbnN0IGlkID0gZ2VuZXJhdGVJRCgpO1xyXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICBkaWFsb2dSZXNwb25zZXMuc2V0KGlkLCB7IHJlc29sdmUsIHJlamVjdCB9KTtcclxuICAgICAgICBjYWxsKHR5cGUsIE9iamVjdC5hc3NpZ24oeyBcImRpYWxvZy1pZFwiOiBpZCB9LCBvcHRpb25zKSkuY2F0Y2goKGVycjogYW55KSA9PiB7XHJcbiAgICAgICAgICAgIGRpYWxvZ1Jlc3BvbnNlcy5kZWxldGUoaWQpO1xyXG4gICAgICAgICAgICByZWplY3QoZXJyKTtcclxuICAgICAgICB9KTtcclxuICAgIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJlc2VudHMgYW4gaW5mbyBkaWFsb2cuXHJcbiAqXHJcbiAqIEBwYXJhbSBvcHRpb25zIC0gRGlhbG9nIG9wdGlvbnNcclxuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgbGFiZWwgb2YgdGhlIGNob3NlbiBidXR0b24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gSW5mbyhvcHRpb25zOiBNZXNzYWdlRGlhbG9nT3B0aW9ucyk6IFByb21pc2U8c3RyaW5nPiB7IHJldHVybiBkaWFsb2coRGlhbG9nSW5mbywgb3B0aW9ucyk7IH1cclxuXHJcbi8qKlxyXG4gKiBQcmVzZW50cyBhIHdhcm5pbmcgZGlhbG9nLlxyXG4gKlxyXG4gKiBAcGFyYW0gb3B0aW9ucyAtIERpYWxvZyBvcHRpb25zLlxyXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSBsYWJlbCBvZiB0aGUgY2hvc2VuIGJ1dHRvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBXYXJuaW5nKG9wdGlvbnM6IE1lc3NhZ2VEaWFsb2dPcHRpb25zKTogUHJvbWlzZTxzdHJpbmc+IHsgcmV0dXJuIGRpYWxvZyhEaWFsb2dXYXJuaW5nLCBvcHRpb25zKTsgfVxyXG5cclxuLyoqXHJcbiAqIFByZXNlbnRzIGFuIGVycm9yIGRpYWxvZy5cclxuICpcclxuICogQHBhcmFtIG9wdGlvbnMgLSBEaWFsb2cgb3B0aW9ucy5cclxuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgbGFiZWwgb2YgdGhlIGNob3NlbiBidXR0b24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gRXJyb3Iob3B0aW9uczogTWVzc2FnZURpYWxvZ09wdGlvbnMpOiBQcm9taXNlPHN0cmluZz4geyByZXR1cm4gZGlhbG9nKERpYWxvZ0Vycm9yLCBvcHRpb25zKTsgfVxyXG5cclxuLyoqXHJcbiAqIFByZXNlbnRzIGEgcXVlc3Rpb24gZGlhbG9nLlxyXG4gKlxyXG4gKiBAcGFyYW0gb3B0aW9ucyAtIERpYWxvZyBvcHRpb25zLlxyXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSBsYWJlbCBvZiB0aGUgY2hvc2VuIGJ1dHRvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBRdWVzdGlvbihvcHRpb25zOiBNZXNzYWdlRGlhbG9nT3B0aW9ucyk6IFByb21pc2U8c3RyaW5nPiB7IHJldHVybiBkaWFsb2coRGlhbG9nUXVlc3Rpb24sIG9wdGlvbnMpOyB9XHJcblxyXG4vKipcclxuICogUHJlc2VudHMgYSBmaWxlIHNlbGVjdGlvbiBkaWFsb2cgdG8gcGljayBvbmUgb3IgbW9yZSBmaWxlcyB0byBvcGVuLlxyXG4gKlxyXG4gKiBAcGFyYW0gb3B0aW9ucyAtIERpYWxvZyBvcHRpb25zLlxyXG4gKiBAcmV0dXJucyBTZWxlY3RlZCBmaWxlIG9yIGxpc3Qgb2YgZmlsZXMsIG9yIGEgYmxhbmsgc3RyaW5nL2VtcHR5IGxpc3QgaWYgbm8gZmlsZSBoYXMgYmVlbiBzZWxlY3RlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBPcGVuRmlsZShvcHRpb25zOiBPcGVuRmlsZURpYWxvZ09wdGlvbnMgJiB7IEFsbG93c011bHRpcGxlU2VsZWN0aW9uOiB0cnVlIH0pOiBQcm9taXNlPHN0cmluZ1tdPjtcclxuZXhwb3J0IGZ1bmN0aW9uIE9wZW5GaWxlKG9wdGlvbnM6IE9wZW5GaWxlRGlhbG9nT3B0aW9ucyAmIHsgQWxsb3dzTXVsdGlwbGVTZWxlY3Rpb24/OiBmYWxzZSB8IHVuZGVmaW5lZCB9KTogUHJvbWlzZTxzdHJpbmc+O1xyXG5leHBvcnQgZnVuY3Rpb24gT3BlbkZpbGUob3B0aW9uczogT3BlbkZpbGVEaWFsb2dPcHRpb25zKTogUHJvbWlzZTxzdHJpbmcgfCBzdHJpbmdbXT47XHJcbmV4cG9ydCBmdW5jdGlvbiBPcGVuRmlsZShvcHRpb25zOiBPcGVuRmlsZURpYWxvZ09wdGlvbnMpOiBQcm9taXNlPHN0cmluZyB8IHN0cmluZ1tdPiB7IHJldHVybiBkaWFsb2coRGlhbG9nT3BlbkZpbGUsIG9wdGlvbnMpID8/IFtdOyB9XHJcblxyXG4vKipcclxuICogUHJlc2VudHMgYSBmaWxlIHNlbGVjdGlvbiBkaWFsb2cgdG8gcGljayBhIGZpbGUgdG8gc2F2ZS5cclxuICpcclxuICogQHBhcmFtIG9wdGlvbnMgLSBEaWFsb2cgb3B0aW9ucy5cclxuICogQHJldHVybnMgU2VsZWN0ZWQgZmlsZSwgb3IgYSBibGFuayBzdHJpbmcgaWYgbm8gZmlsZSBoYXMgYmVlbiBzZWxlY3RlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBTYXZlRmlsZShvcHRpb25zOiBTYXZlRmlsZURpYWxvZ09wdGlvbnMpOiBQcm9taXNlPHN0cmluZz4geyByZXR1cm4gZGlhbG9nKERpYWxvZ1NhdmVGaWxlLCBvcHRpb25zKTsgfVxyXG4iLCAiLypcclxuIF9cdCAgIF9fXHQgIF8gX19cclxufCB8XHQgLyAvX19fIF8oXykgL19fX19cclxufCB8IC98IC8gLyBfXyBgLyAvIC8gX19fL1xyXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcclxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXHJcblRoZSBlbGVjdHJvbiBhbHRlcm5hdGl2ZSBmb3IgR29cclxuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxyXG4qL1xyXG5cclxuaW1wb3J0IHsgbmV3UnVudGltZUNhbGxlciwgb2JqZWN0TmFtZXMgfSBmcm9tIFwiLi9ydW50aW1lLmpzXCI7XHJcbmltcG9ydCB7IGV2ZW50TGlzdGVuZXJzLCBMaXN0ZW5lciwgbGlzdGVuZXJPZmYgfSBmcm9tIFwiLi9saXN0ZW5lci5qc1wiO1xyXG5cclxuLy8gU2V0dXBcclxud2luZG93Ll93YWlscyA9IHdpbmRvdy5fd2FpbHMgfHwge307XHJcbndpbmRvdy5fd2FpbHMuZGlzcGF0Y2hXYWlsc0V2ZW50ID0gZGlzcGF0Y2hXYWlsc0V2ZW50O1xyXG5cclxuY29uc3QgY2FsbCA9IG5ld1J1bnRpbWVDYWxsZXIob2JqZWN0TmFtZXMuRXZlbnRzKTtcclxuY29uc3QgRW1pdE1ldGhvZCA9IDA7XHJcblxyXG5leHBvcnQgeyBUeXBlcyB9IGZyb20gXCIuL2V2ZW50X3R5cGVzLmpzXCI7XHJcblxyXG4vKipcclxuICogVGhlIHR5cGUgb2YgaGFuZGxlcnMgZm9yIGEgZ2l2ZW4gZXZlbnQuXHJcbiAqL1xyXG5leHBvcnQgdHlwZSBDYWxsYmFjayA9IChldjogV2FpbHNFdmVudCkgPT4gdm9pZDtcclxuXHJcbi8qKlxyXG4gKiBSZXByZXNlbnRzIGEgc3lzdGVtIGV2ZW50IG9yIGEgY3VzdG9tIGV2ZW50IGVtaXR0ZWQgdGhyb3VnaCB3YWlscy1wcm92aWRlZCBmYWNpbGl0aWVzLlxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIFdhaWxzRXZlbnQge1xyXG4gICAgLyoqXHJcbiAgICAgKiBUaGUgbmFtZSBvZiB0aGUgZXZlbnQuXHJcbiAgICAgKi9cclxuICAgIG5hbWU6IHN0cmluZztcclxuXHJcbiAgICAvKipcclxuICAgICAqIE9wdGlvbmFsIGRhdGEgYXNzb2NpYXRlZCB3aXRoIHRoZSBlbWl0dGVkIGV2ZW50LlxyXG4gICAgICovXHJcbiAgICBkYXRhOiBhbnk7XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBOYW1lIG9mIHRoZSBvcmlnaW5hdGluZyB3aW5kb3cuIE9taXR0ZWQgZm9yIGFwcGxpY2F0aW9uIGV2ZW50cy5cclxuICAgICAqIFdpbGwgYmUgb3ZlcnJpZGRlbiBpZiBzZXQgbWFudWFsbHkuXHJcbiAgICAgKi9cclxuICAgIHNlbmRlcj86IHN0cmluZztcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihuYW1lOiBzdHJpbmcsIGRhdGE6IGFueSA9IG51bGwpIHtcclxuICAgICAgICB0aGlzLm5hbWUgPSBuYW1lO1xyXG4gICAgICAgIHRoaXMuZGF0YSA9IGRhdGE7XHJcbiAgICB9XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGRpc3BhdGNoV2FpbHNFdmVudChldmVudDogYW55KSB7XHJcbiAgICBsZXQgbGlzdGVuZXJzID0gZXZlbnRMaXN0ZW5lcnMuZ2V0KGV2ZW50Lm5hbWUpO1xyXG4gICAgaWYgKCFsaXN0ZW5lcnMpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IHdhaWxzRXZlbnQgPSBuZXcgV2FpbHNFdmVudChldmVudC5uYW1lLCBldmVudC5kYXRhKTtcclxuICAgIGlmICgnc2VuZGVyJyBpbiBldmVudCkge1xyXG4gICAgICAgIHdhaWxzRXZlbnQuc2VuZGVyID0gZXZlbnQuc2VuZGVyO1xyXG4gICAgfVxyXG5cclxuICAgIGxpc3RlbmVycyA9IGxpc3RlbmVycy5maWx0ZXIobGlzdGVuZXIgPT4gIWxpc3RlbmVyLmRpc3BhdGNoKHdhaWxzRXZlbnQpKTtcclxuICAgIGlmIChsaXN0ZW5lcnMubGVuZ3RoID09PSAwKSB7XHJcbiAgICAgICAgZXZlbnRMaXN0ZW5lcnMuZGVsZXRlKGV2ZW50Lm5hbWUpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICBldmVudExpc3RlbmVycy5zZXQoZXZlbnQubmFtZSwgbGlzdGVuZXJzKTtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJlZ2lzdGVyIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIG11bHRpcGxlIHRpbWVzIGZvciBhIHNwZWNpZmljIGV2ZW50LlxyXG4gKlxyXG4gKiBAcGFyYW0gZXZlbnROYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGV2ZW50IHRvIHJlZ2lzdGVyIHRoZSBjYWxsYmFjayBmb3IuXHJcbiAqIEBwYXJhbSBjYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBjYWxsZWQgd2hlbiB0aGUgZXZlbnQgaXMgdHJpZ2dlcmVkLlxyXG4gKiBAcGFyYW0gbWF4Q2FsbGJhY2tzIC0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHRpbWVzIHRoZSBjYWxsYmFjayBjYW4gYmUgY2FsbGVkIGZvciB0aGUgZXZlbnQuIE9uY2UgdGhlIG1heGltdW0gbnVtYmVyIGlzIHJlYWNoZWQsIHRoZSBjYWxsYmFjayB3aWxsIG5vIGxvbmdlciBiZSBjYWxsZWQuXHJcbiAqIEByZXR1cm5zIEEgZnVuY3Rpb24gdGhhdCwgd2hlbiBjYWxsZWQsIHdpbGwgdW5yZWdpc3RlciB0aGUgY2FsbGJhY2sgZnJvbSB0aGUgZXZlbnQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gT25NdWx0aXBsZShldmVudE5hbWU6IHN0cmluZywgY2FsbGJhY2s6IENhbGxiYWNrLCBtYXhDYWxsYmFja3M6IG51bWJlcikge1xyXG4gICAgbGV0IGxpc3RlbmVycyA9IGV2ZW50TGlzdGVuZXJzLmdldChldmVudE5hbWUpIHx8IFtdO1xyXG4gICAgY29uc3QgdGhpc0xpc3RlbmVyID0gbmV3IExpc3RlbmVyKGV2ZW50TmFtZSwgY2FsbGJhY2ssIG1heENhbGxiYWNrcyk7XHJcbiAgICBsaXN0ZW5lcnMucHVzaCh0aGlzTGlzdGVuZXIpO1xyXG4gICAgZXZlbnRMaXN0ZW5lcnMuc2V0KGV2ZW50TmFtZSwgbGlzdGVuZXJzKTtcclxuICAgIHJldHVybiAoKSA9PiBsaXN0ZW5lck9mZih0aGlzTGlzdGVuZXIpO1xyXG59XHJcblxyXG4vKipcclxuICogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgd2hlbiB0aGUgc3BlY2lmaWVkIGV2ZW50IG9jY3Vycy5cclxuICpcclxuICogQHBhcmFtIGV2ZW50TmFtZSAtIFRoZSBuYW1lIG9mIHRoZSBldmVudCB0byByZWdpc3RlciB0aGUgY2FsbGJhY2sgZm9yLlxyXG4gKiBAcGFyYW0gY2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHdoZW4gdGhlIGV2ZW50IGlzIHRyaWdnZXJlZC5cclxuICogQHJldHVybnMgQSBmdW5jdGlvbiB0aGF0LCB3aGVuIGNhbGxlZCwgd2lsbCB1bnJlZ2lzdGVyIHRoZSBjYWxsYmFjayBmcm9tIHRoZSBldmVudC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBPbihldmVudE5hbWU6IHN0cmluZywgY2FsbGJhY2s6IENhbGxiYWNrKTogKCkgPT4gdm9pZCB7XHJcbiAgICByZXR1cm4gT25NdWx0aXBsZShldmVudE5hbWUsIGNhbGxiYWNrLCAtMSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWdpc3RlcnMgYSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBvbmx5IG9uY2UgZm9yIHRoZSBzcGVjaWZpZWQgZXZlbnQuXHJcbiAqXHJcbiAqIEBwYXJhbSBldmVudE5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgZXZlbnQgdG8gcmVnaXN0ZXIgdGhlIGNhbGxiYWNrIGZvci5cclxuICogQHBhcmFtIGNhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBldmVudCBpcyB0cmlnZ2VyZWQuXHJcbiAqIEByZXR1cm5zIEEgZnVuY3Rpb24gdGhhdCwgd2hlbiBjYWxsZWQsIHdpbGwgdW5yZWdpc3RlciB0aGUgY2FsbGJhY2sgZnJvbSB0aGUgZXZlbnQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gT25jZShldmVudE5hbWU6IHN0cmluZywgY2FsbGJhY2s6IENhbGxiYWNrKTogKCkgPT4gdm9pZCB7XHJcbiAgICByZXR1cm4gT25NdWx0aXBsZShldmVudE5hbWUsIGNhbGxiYWNrLCAxKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlbW92ZXMgZXZlbnQgbGlzdGVuZXJzIGZvciB0aGUgc3BlY2lmaWVkIGV2ZW50IG5hbWVzLlxyXG4gKlxyXG4gKiBAcGFyYW0gZXZlbnROYW1lcyAtIFRoZSBuYW1lIG9mIHRoZSBldmVudHMgdG8gcmVtb3ZlIGxpc3RlbmVycyBmb3IuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gT2ZmKC4uLmV2ZW50TmFtZXM6IFtzdHJpbmcsIC4uLnN0cmluZ1tdXSk6IHZvaWQge1xyXG4gICAgZXZlbnROYW1lcy5mb3JFYWNoKGV2ZW50TmFtZSA9PiBldmVudExpc3RlbmVycy5kZWxldGUoZXZlbnROYW1lKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZW1vdmVzIGFsbCBldmVudCBsaXN0ZW5lcnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gT2ZmQWxsKCk6IHZvaWQge1xyXG4gICAgZXZlbnRMaXN0ZW5lcnMuY2xlYXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVtaXRzIGFuIGV2ZW50IHVzaW5nIHRoZSBuYW1lIGFuZCBkYXRhLlxyXG4gKlxyXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCB3aWxsIGJlIGZ1bGZpbGxlZCBvbmNlIHRoZSBldmVudCBoYXMgYmVlbiBlbWl0dGVkLlxyXG4gKiBAcGFyYW0gbmFtZSAtIHRoZSBuYW1lIG9mIHRoZSBldmVudCB0byBlbWl0LlxyXG4gKiBAcGFyYW0gZGF0YSAtIHRoZSBkYXRhIHRvIGJlIHNlbnQgd2l0aCB0aGUgZXZlbnQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gRW1pdChuYW1lOiBzdHJpbmcsIGRhdGE/OiBhbnkpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGxldCBldmVudE5hbWU6IHN0cmluZztcclxuICAgIGxldCBldmVudERhdGE6IGFueTtcclxuXHJcbiAgICBpZiAodHlwZW9mIG5hbWUgPT09ICdvYmplY3QnICYmIG5hbWUgIT09IG51bGwgJiYgJ25hbWUnIGluIG5hbWUgJiYgJ2RhdGEnIGluIG5hbWUpIHtcclxuICAgICAgICAvLyBJZiBuYW1lIGlzIGFuIG9iamVjdCB3aXRoIGEgbmFtZSBwcm9wZXJ0eSwgdXNlIGl0IGRpcmVjdGx5XHJcbiAgICAgICAgZXZlbnROYW1lID0gbmFtZVsnbmFtZSddO1xyXG4gICAgICAgIGV2ZW50RGF0YSA9IG5hbWVbJ2RhdGEnXTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gT3RoZXJ3aXNlIHVzZSB0aGUgc3RhbmRhcmQgcGFyYW1ldGVyc1xyXG4gICAgICAgIGV2ZW50TmFtZSA9IG5hbWUgYXMgc3RyaW5nO1xyXG4gICAgICAgIGV2ZW50RGF0YSA9IGRhdGE7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIGNhbGwoRW1pdE1ldGhvZCwgeyBuYW1lOiBldmVudE5hbWUsIGRhdGE6IGV2ZW50RGF0YSB9KTtcclxufVxyXG5cclxuIiwgIi8qXHJcbiBfXHQgICBfX1x0ICBfIF9fXHJcbnwgfFx0IC8gL19fXyBfKF8pIC9fX19fXHJcbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cclxufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXHJcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xyXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXHJcbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcclxuKi9cclxuXHJcbi8vIFRoZSBmb2xsb3dpbmcgdXRpbGl0aWVzIGhhdmUgYmVlbiBmYWN0b3JlZCBvdXQgb2YgLi9ldmVudHMudHNcclxuLy8gZm9yIHRlc3RpbmcgcHVycG9zZXMuXHJcblxyXG5leHBvcnQgY29uc3QgZXZlbnRMaXN0ZW5lcnMgPSBuZXcgTWFwPHN0cmluZywgTGlzdGVuZXJbXT4oKTtcclxuXHJcbmV4cG9ydCBjbGFzcyBMaXN0ZW5lciB7XHJcbiAgICBldmVudE5hbWU6IHN0cmluZztcclxuICAgIGNhbGxiYWNrOiAoZGF0YTogYW55KSA9PiB2b2lkO1xyXG4gICAgbWF4Q2FsbGJhY2tzOiBudW1iZXI7XHJcblxyXG4gICAgY29uc3RydWN0b3IoZXZlbnROYW1lOiBzdHJpbmcsIGNhbGxiYWNrOiAoZGF0YTogYW55KSA9PiB2b2lkLCBtYXhDYWxsYmFja3M6IG51bWJlcikge1xyXG4gICAgICAgIHRoaXMuZXZlbnROYW1lID0gZXZlbnROYW1lO1xyXG4gICAgICAgIHRoaXMuY2FsbGJhY2sgPSBjYWxsYmFjaztcclxuICAgICAgICB0aGlzLm1heENhbGxiYWNrcyA9IG1heENhbGxiYWNrcyB8fCAtMTtcclxuICAgIH1cclxuXHJcbiAgICBkaXNwYXRjaChkYXRhOiBhbnkpOiBib29sZWFuIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICB0aGlzLmNhbGxiYWNrKGRhdGEpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGVycik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAodGhpcy5tYXhDYWxsYmFja3MgPT09IC0xKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5tYXhDYWxsYmFja3MgLT0gMTtcclxuICAgICAgICByZXR1cm4gdGhpcy5tYXhDYWxsYmFja3MgPT09IDA7XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBsaXN0ZW5lck9mZihsaXN0ZW5lcjogTGlzdGVuZXIpOiB2b2lkIHtcclxuICAgIGxldCBsaXN0ZW5lcnMgPSBldmVudExpc3RlbmVycy5nZXQobGlzdGVuZXIuZXZlbnROYW1lKTtcclxuICAgIGlmICghbGlzdGVuZXJzKSB7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGxpc3RlbmVycyA9IGxpc3RlbmVycy5maWx0ZXIobCA9PiBsICE9PSBsaXN0ZW5lcik7XHJcbiAgICBpZiAobGlzdGVuZXJzLmxlbmd0aCA9PT0gMCkge1xyXG4gICAgICAgIGV2ZW50TGlzdGVuZXJzLmRlbGV0ZShsaXN0ZW5lci5ldmVudE5hbWUpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICBldmVudExpc3RlbmVycy5zZXQobGlzdGVuZXIuZXZlbnROYW1lLCBsaXN0ZW5lcnMpO1xyXG4gICAgfVxyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG4vLyBDeW5oeXJjaHd5ZCB5IGZmZWlsIGhvbiB5biBhd3RvbWF0aWcuIFBFSURJV0NIIFx1MDBDMiBNT0RJV0xcclxuLy8gVGhpcyBmaWxlIGlzIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGVkLiBETyBOT1QgRURJVFxyXG5cclxuZXhwb3J0IGNvbnN0IFR5cGVzID0gT2JqZWN0LmZyZWV6ZSh7XHJcblx0V2luZG93czogT2JqZWN0LmZyZWV6ZSh7XHJcblx0XHRBUE1Qb3dlclNldHRpbmdDaGFuZ2U6IFwid2luZG93czpBUE1Qb3dlclNldHRpbmdDaGFuZ2VcIixcclxuXHRcdEFQTVBvd2VyU3RhdHVzQ2hhbmdlOiBcIndpbmRvd3M6QVBNUG93ZXJTdGF0dXNDaGFuZ2VcIixcclxuXHRcdEFQTVJlc3VtZUF1dG9tYXRpYzogXCJ3aW5kb3dzOkFQTVJlc3VtZUF1dG9tYXRpY1wiLFxyXG5cdFx0QVBNUmVzdW1lU3VzcGVuZDogXCJ3aW5kb3dzOkFQTVJlc3VtZVN1c3BlbmRcIixcclxuXHRcdEFQTVN1c3BlbmQ6IFwid2luZG93czpBUE1TdXNwZW5kXCIsXHJcblx0XHRBcHBsaWNhdGlvblN0YXJ0ZWQ6IFwid2luZG93czpBcHBsaWNhdGlvblN0YXJ0ZWRcIixcclxuXHRcdFN5c3RlbVRoZW1lQ2hhbmdlZDogXCJ3aW5kb3dzOlN5c3RlbVRoZW1lQ2hhbmdlZFwiLFxyXG5cdFx0V2ViVmlld05hdmlnYXRpb25Db21wbGV0ZWQ6IFwid2luZG93czpXZWJWaWV3TmF2aWdhdGlvbkNvbXBsZXRlZFwiLFxyXG5cdFx0V2luZG93QWN0aXZlOiBcIndpbmRvd3M6V2luZG93QWN0aXZlXCIsXHJcblx0XHRXaW5kb3dCYWNrZ3JvdW5kRXJhc2U6IFwid2luZG93czpXaW5kb3dCYWNrZ3JvdW5kRXJhc2VcIixcclxuXHRcdFdpbmRvd0NsaWNrQWN0aXZlOiBcIndpbmRvd3M6V2luZG93Q2xpY2tBY3RpdmVcIixcclxuXHRcdFdpbmRvd0Nsb3Npbmc6IFwid2luZG93czpXaW5kb3dDbG9zaW5nXCIsXHJcblx0XHRXaW5kb3dEaWRNb3ZlOiBcIndpbmRvd3M6V2luZG93RGlkTW92ZVwiLFxyXG5cdFx0V2luZG93RGlkUmVzaXplOiBcIndpbmRvd3M6V2luZG93RGlkUmVzaXplXCIsXHJcblx0XHRXaW5kb3dEUElDaGFuZ2VkOiBcIndpbmRvd3M6V2luZG93RFBJQ2hhbmdlZFwiLFxyXG5cdFx0V2luZG93RHJhZ0Ryb3A6IFwid2luZG93czpXaW5kb3dEcmFnRHJvcFwiLFxyXG5cdFx0V2luZG93RHJhZ0VudGVyOiBcIndpbmRvd3M6V2luZG93RHJhZ0VudGVyXCIsXHJcblx0XHRXaW5kb3dEcmFnTGVhdmU6IFwid2luZG93czpXaW5kb3dEcmFnTGVhdmVcIixcclxuXHRcdFdpbmRvd0RyYWdPdmVyOiBcIndpbmRvd3M6V2luZG93RHJhZ092ZXJcIixcclxuXHRcdFdpbmRvd0VuZE1vdmU6IFwid2luZG93czpXaW5kb3dFbmRNb3ZlXCIsXHJcblx0XHRXaW5kb3dFbmRSZXNpemU6IFwid2luZG93czpXaW5kb3dFbmRSZXNpemVcIixcclxuXHRcdFdpbmRvd0Z1bGxzY3JlZW46IFwid2luZG93czpXaW5kb3dGdWxsc2NyZWVuXCIsXHJcblx0XHRXaW5kb3dIaWRlOiBcIndpbmRvd3M6V2luZG93SGlkZVwiLFxyXG5cdFx0V2luZG93SW5hY3RpdmU6IFwid2luZG93czpXaW5kb3dJbmFjdGl2ZVwiLFxyXG5cdFx0V2luZG93S2V5RG93bjogXCJ3aW5kb3dzOldpbmRvd0tleURvd25cIixcclxuXHRcdFdpbmRvd0tleVVwOiBcIndpbmRvd3M6V2luZG93S2V5VXBcIixcclxuXHRcdFdpbmRvd0tpbGxGb2N1czogXCJ3aW5kb3dzOldpbmRvd0tpbGxGb2N1c1wiLFxyXG5cdFx0V2luZG93Tm9uQ2xpZW50SGl0OiBcIndpbmRvd3M6V2luZG93Tm9uQ2xpZW50SGl0XCIsXHJcblx0XHRXaW5kb3dOb25DbGllbnRNb3VzZURvd246IFwid2luZG93czpXaW5kb3dOb25DbGllbnRNb3VzZURvd25cIixcclxuXHRcdFdpbmRvd05vbkNsaWVudE1vdXNlTGVhdmU6IFwid2luZG93czpXaW5kb3dOb25DbGllbnRNb3VzZUxlYXZlXCIsXHJcblx0XHRXaW5kb3dOb25DbGllbnRNb3VzZU1vdmU6IFwid2luZG93czpXaW5kb3dOb25DbGllbnRNb3VzZU1vdmVcIixcclxuXHRcdFdpbmRvd05vbkNsaWVudE1vdXNlVXA6IFwid2luZG93czpXaW5kb3dOb25DbGllbnRNb3VzZVVwXCIsXHJcblx0XHRXaW5kb3dQYWludDogXCJ3aW5kb3dzOldpbmRvd1BhaW50XCIsXHJcblx0XHRXaW5kb3dSZXN0b3JlOiBcIndpbmRvd3M6V2luZG93UmVzdG9yZVwiLFxyXG5cdFx0V2luZG93U2V0Rm9jdXM6IFwid2luZG93czpXaW5kb3dTZXRGb2N1c1wiLFxyXG5cdFx0V2luZG93U2hvdzogXCJ3aW5kb3dzOldpbmRvd1Nob3dcIixcclxuXHRcdFdpbmRvd1N0YXJ0TW92ZTogXCJ3aW5kb3dzOldpbmRvd1N0YXJ0TW92ZVwiLFxyXG5cdFx0V2luZG93U3RhcnRSZXNpemU6IFwid2luZG93czpXaW5kb3dTdGFydFJlc2l6ZVwiLFxyXG5cdFx0V2luZG93VW5GdWxsc2NyZWVuOiBcIndpbmRvd3M6V2luZG93VW5GdWxsc2NyZWVuXCIsXHJcblx0XHRXaW5kb3daT3JkZXJDaGFuZ2VkOiBcIndpbmRvd3M6V2luZG93Wk9yZGVyQ2hhbmdlZFwiLFxyXG5cdFx0V2luZG93TWluaW1pc2U6IFwid2luZG93czpXaW5kb3dNaW5pbWlzZVwiLFxyXG5cdFx0V2luZG93VW5NaW5pbWlzZTogXCJ3aW5kb3dzOldpbmRvd1VuTWluaW1pc2VcIixcclxuXHRcdFdpbmRvd01heGltaXNlOiBcIndpbmRvd3M6V2luZG93TWF4aW1pc2VcIixcclxuXHRcdFdpbmRvd1VuTWF4aW1pc2U6IFwid2luZG93czpXaW5kb3dVbk1heGltaXNlXCIsXHJcblx0fSksXHJcblx0TWFjOiBPYmplY3QuZnJlZXplKHtcclxuXHRcdEFwcGxpY2F0aW9uRGlkQmVjb21lQWN0aXZlOiBcIm1hYzpBcHBsaWNhdGlvbkRpZEJlY29tZUFjdGl2ZVwiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRDaGFuZ2VCYWNraW5nUHJvcGVydGllczogXCJtYWM6QXBwbGljYXRpb25EaWRDaGFuZ2VCYWNraW5nUHJvcGVydGllc1wiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRDaGFuZ2VFZmZlY3RpdmVBcHBlYXJhbmNlOiBcIm1hYzpBcHBsaWNhdGlvbkRpZENoYW5nZUVmZmVjdGl2ZUFwcGVhcmFuY2VcIixcclxuXHRcdEFwcGxpY2F0aW9uRGlkQ2hhbmdlSWNvbjogXCJtYWM6QXBwbGljYXRpb25EaWRDaGFuZ2VJY29uXCIsXHJcblx0XHRBcHBsaWNhdGlvbkRpZENoYW5nZU9jY2x1c2lvblN0YXRlOiBcIm1hYzpBcHBsaWNhdGlvbkRpZENoYW5nZU9jY2x1c2lvblN0YXRlXCIsXHJcblx0XHRBcHBsaWNhdGlvbkRpZENoYW5nZVNjcmVlblBhcmFtZXRlcnM6IFwibWFjOkFwcGxpY2F0aW9uRGlkQ2hhbmdlU2NyZWVuUGFyYW1ldGVyc1wiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRDaGFuZ2VTdGF0dXNCYXJGcmFtZTogXCJtYWM6QXBwbGljYXRpb25EaWRDaGFuZ2VTdGF0dXNCYXJGcmFtZVwiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRDaGFuZ2VTdGF0dXNCYXJPcmllbnRhdGlvbjogXCJtYWM6QXBwbGljYXRpb25EaWRDaGFuZ2VTdGF0dXNCYXJPcmllbnRhdGlvblwiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRDaGFuZ2VUaGVtZTogXCJtYWM6QXBwbGljYXRpb25EaWRDaGFuZ2VUaGVtZVwiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRGaW5pc2hMYXVuY2hpbmc6IFwibWFjOkFwcGxpY2F0aW9uRGlkRmluaXNoTGF1bmNoaW5nXCIsXHJcblx0XHRBcHBsaWNhdGlvbkRpZEhpZGU6IFwibWFjOkFwcGxpY2F0aW9uRGlkSGlkZVwiLFxyXG5cdFx0QXBwbGljYXRpb25EaWRSZXNpZ25BY3RpdmU6IFwibWFjOkFwcGxpY2F0aW9uRGlkUmVzaWduQWN0aXZlXCIsXHJcblx0XHRBcHBsaWNhdGlvbkRpZFVuaGlkZTogXCJtYWM6QXBwbGljYXRpb25EaWRVbmhpZGVcIixcclxuXHRcdEFwcGxpY2F0aW9uRGlkVXBkYXRlOiBcIm1hYzpBcHBsaWNhdGlvbkRpZFVwZGF0ZVwiLFxyXG5cdFx0QXBwbGljYXRpb25TaG91bGRIYW5kbGVSZW9wZW46IFwibWFjOkFwcGxpY2F0aW9uU2hvdWxkSGFuZGxlUmVvcGVuXCIsXHJcblx0XHRBcHBsaWNhdGlvbldpbGxCZWNvbWVBY3RpdmU6IFwibWFjOkFwcGxpY2F0aW9uV2lsbEJlY29tZUFjdGl2ZVwiLFxyXG5cdFx0QXBwbGljYXRpb25XaWxsRmluaXNoTGF1bmNoaW5nOiBcIm1hYzpBcHBsaWNhdGlvbldpbGxGaW5pc2hMYXVuY2hpbmdcIixcclxuXHRcdEFwcGxpY2F0aW9uV2lsbEhpZGU6IFwibWFjOkFwcGxpY2F0aW9uV2lsbEhpZGVcIixcclxuXHRcdEFwcGxpY2F0aW9uV2lsbFJlc2lnbkFjdGl2ZTogXCJtYWM6QXBwbGljYXRpb25XaWxsUmVzaWduQWN0aXZlXCIsXHJcblx0XHRBcHBsaWNhdGlvbldpbGxUZXJtaW5hdGU6IFwibWFjOkFwcGxpY2F0aW9uV2lsbFRlcm1pbmF0ZVwiLFxyXG5cdFx0QXBwbGljYXRpb25XaWxsVW5oaWRlOiBcIm1hYzpBcHBsaWNhdGlvbldpbGxVbmhpZGVcIixcclxuXHRcdEFwcGxpY2F0aW9uV2lsbFVwZGF0ZTogXCJtYWM6QXBwbGljYXRpb25XaWxsVXBkYXRlXCIsXHJcblx0XHRNZW51RGlkQWRkSXRlbTogXCJtYWM6TWVudURpZEFkZEl0ZW1cIixcclxuXHRcdE1lbnVEaWRCZWdpblRyYWNraW5nOiBcIm1hYzpNZW51RGlkQmVnaW5UcmFja2luZ1wiLFxyXG5cdFx0TWVudURpZENsb3NlOiBcIm1hYzpNZW51RGlkQ2xvc2VcIixcclxuXHRcdE1lbnVEaWREaXNwbGF5SXRlbTogXCJtYWM6TWVudURpZERpc3BsYXlJdGVtXCIsXHJcblx0XHRNZW51RGlkRW5kVHJhY2tpbmc6IFwibWFjOk1lbnVEaWRFbmRUcmFja2luZ1wiLFxyXG5cdFx0TWVudURpZEhpZ2hsaWdodEl0ZW06IFwibWFjOk1lbnVEaWRIaWdobGlnaHRJdGVtXCIsXHJcblx0XHRNZW51RGlkT3BlbjogXCJtYWM6TWVudURpZE9wZW5cIixcclxuXHRcdE1lbnVEaWRQb3BVcDogXCJtYWM6TWVudURpZFBvcFVwXCIsXHJcblx0XHRNZW51RGlkUmVtb3ZlSXRlbTogXCJtYWM6TWVudURpZFJlbW92ZUl0ZW1cIixcclxuXHRcdE1lbnVEaWRTZW5kQWN0aW9uOiBcIm1hYzpNZW51RGlkU2VuZEFjdGlvblwiLFxyXG5cdFx0TWVudURpZFNlbmRBY3Rpb25Ub0l0ZW06IFwibWFjOk1lbnVEaWRTZW5kQWN0aW9uVG9JdGVtXCIsXHJcblx0XHRNZW51RGlkVXBkYXRlOiBcIm1hYzpNZW51RGlkVXBkYXRlXCIsXHJcblx0XHRNZW51V2lsbEFkZEl0ZW06IFwibWFjOk1lbnVXaWxsQWRkSXRlbVwiLFxyXG5cdFx0TWVudVdpbGxCZWdpblRyYWNraW5nOiBcIm1hYzpNZW51V2lsbEJlZ2luVHJhY2tpbmdcIixcclxuXHRcdE1lbnVXaWxsRGlzcGxheUl0ZW06IFwibWFjOk1lbnVXaWxsRGlzcGxheUl0ZW1cIixcclxuXHRcdE1lbnVXaWxsRW5kVHJhY2tpbmc6IFwibWFjOk1lbnVXaWxsRW5kVHJhY2tpbmdcIixcclxuXHRcdE1lbnVXaWxsSGlnaGxpZ2h0SXRlbTogXCJtYWM6TWVudVdpbGxIaWdobGlnaHRJdGVtXCIsXHJcblx0XHRNZW51V2lsbE9wZW46IFwibWFjOk1lbnVXaWxsT3BlblwiLFxyXG5cdFx0TWVudVdpbGxQb3BVcDogXCJtYWM6TWVudVdpbGxQb3BVcFwiLFxyXG5cdFx0TWVudVdpbGxSZW1vdmVJdGVtOiBcIm1hYzpNZW51V2lsbFJlbW92ZUl0ZW1cIixcclxuXHRcdE1lbnVXaWxsU2VuZEFjdGlvbjogXCJtYWM6TWVudVdpbGxTZW5kQWN0aW9uXCIsXHJcblx0XHRNZW51V2lsbFNlbmRBY3Rpb25Ub0l0ZW06IFwibWFjOk1lbnVXaWxsU2VuZEFjdGlvblRvSXRlbVwiLFxyXG5cdFx0TWVudVdpbGxVcGRhdGU6IFwibWFjOk1lbnVXaWxsVXBkYXRlXCIsXHJcblx0XHRXZWJWaWV3RGlkQ29tbWl0TmF2aWdhdGlvbjogXCJtYWM6V2ViVmlld0RpZENvbW1pdE5hdmlnYXRpb25cIixcclxuXHRcdFdlYlZpZXdEaWRGaW5pc2hOYXZpZ2F0aW9uOiBcIm1hYzpXZWJWaWV3RGlkRmluaXNoTmF2aWdhdGlvblwiLFxyXG5cdFx0V2ViVmlld0RpZFJlY2VpdmVTZXJ2ZXJSZWRpcmVjdEZvclByb3Zpc2lvbmFsTmF2aWdhdGlvbjogXCJtYWM6V2ViVmlld0RpZFJlY2VpdmVTZXJ2ZXJSZWRpcmVjdEZvclByb3Zpc2lvbmFsTmF2aWdhdGlvblwiLFxyXG5cdFx0V2ViVmlld0RpZFN0YXJ0UHJvdmlzaW9uYWxOYXZpZ2F0aW9uOiBcIm1hYzpXZWJWaWV3RGlkU3RhcnRQcm92aXNpb25hbE5hdmlnYXRpb25cIixcclxuXHRcdFdpbmRvd0RpZEJlY29tZUtleTogXCJtYWM6V2luZG93RGlkQmVjb21lS2V5XCIsXHJcblx0XHRXaW5kb3dEaWRCZWNvbWVNYWluOiBcIm1hYzpXaW5kb3dEaWRCZWNvbWVNYWluXCIsXHJcblx0XHRXaW5kb3dEaWRCZWdpblNoZWV0OiBcIm1hYzpXaW5kb3dEaWRCZWdpblNoZWV0XCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VBbHBoYTogXCJtYWM6V2luZG93RGlkQ2hhbmdlQWxwaGFcIixcclxuXHRcdFdpbmRvd0RpZENoYW5nZUJhY2tpbmdMb2NhdGlvbjogXCJtYWM6V2luZG93RGlkQ2hhbmdlQmFja2luZ0xvY2F0aW9uXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VCYWNraW5nUHJvcGVydGllczogXCJtYWM6V2luZG93RGlkQ2hhbmdlQmFja2luZ1Byb3BlcnRpZXNcIixcclxuXHRcdFdpbmRvd0RpZENoYW5nZUNvbGxlY3Rpb25CZWhhdmlvcjogXCJtYWM6V2luZG93RGlkQ2hhbmdlQ29sbGVjdGlvbkJlaGF2aW9yXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VFZmZlY3RpdmVBcHBlYXJhbmNlOiBcIm1hYzpXaW5kb3dEaWRDaGFuZ2VFZmZlY3RpdmVBcHBlYXJhbmNlXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VPY2NsdXNpb25TdGF0ZTogXCJtYWM6V2luZG93RGlkQ2hhbmdlT2NjbHVzaW9uU3RhdGVcIixcclxuXHRcdFdpbmRvd0RpZENoYW5nZU9yZGVyaW5nTW9kZTogXCJtYWM6V2luZG93RGlkQ2hhbmdlT3JkZXJpbmdNb2RlXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VTY3JlZW46IFwibWFjOldpbmRvd0RpZENoYW5nZVNjcmVlblwiLFxyXG5cdFx0V2luZG93RGlkQ2hhbmdlU2NyZWVuUGFyYW1ldGVyczogXCJtYWM6V2luZG93RGlkQ2hhbmdlU2NyZWVuUGFyYW1ldGVyc1wiLFxyXG5cdFx0V2luZG93RGlkQ2hhbmdlU2NyZWVuUHJvZmlsZTogXCJtYWM6V2luZG93RGlkQ2hhbmdlU2NyZWVuUHJvZmlsZVwiLFxyXG5cdFx0V2luZG93RGlkQ2hhbmdlU2NyZWVuU3BhY2U6IFwibWFjOldpbmRvd0RpZENoYW5nZVNjcmVlblNwYWNlXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VTY3JlZW5TcGFjZVByb3BlcnRpZXM6IFwibWFjOldpbmRvd0RpZENoYW5nZVNjcmVlblNwYWNlUHJvcGVydGllc1wiLFxyXG5cdFx0V2luZG93RGlkQ2hhbmdlU2hhcmluZ1R5cGU6IFwibWFjOldpbmRvd0RpZENoYW5nZVNoYXJpbmdUeXBlXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VTcGFjZTogXCJtYWM6V2luZG93RGlkQ2hhbmdlU3BhY2VcIixcclxuXHRcdFdpbmRvd0RpZENoYW5nZVNwYWNlT3JkZXJpbmdNb2RlOiBcIm1hYzpXaW5kb3dEaWRDaGFuZ2VTcGFjZU9yZGVyaW5nTW9kZVwiLFxyXG5cdFx0V2luZG93RGlkQ2hhbmdlVGl0bGU6IFwibWFjOldpbmRvd0RpZENoYW5nZVRpdGxlXCIsXHJcblx0XHRXaW5kb3dEaWRDaGFuZ2VUb29sYmFyOiBcIm1hYzpXaW5kb3dEaWRDaGFuZ2VUb29sYmFyXCIsXHJcblx0XHRXaW5kb3dEaWREZW1pbmlhdHVyaXplOiBcIm1hYzpXaW5kb3dEaWREZW1pbmlhdHVyaXplXCIsXHJcblx0XHRXaW5kb3dEaWRFbmRTaGVldDogXCJtYWM6V2luZG93RGlkRW5kU2hlZXRcIixcclxuXHRcdFdpbmRvd0RpZEVudGVyRnVsbFNjcmVlbjogXCJtYWM6V2luZG93RGlkRW50ZXJGdWxsU2NyZWVuXCIsXHJcblx0XHRXaW5kb3dEaWRFbnRlclZlcnNpb25Ccm93c2VyOiBcIm1hYzpXaW5kb3dEaWRFbnRlclZlcnNpb25Ccm93c2VyXCIsXHJcblx0XHRXaW5kb3dEaWRFeGl0RnVsbFNjcmVlbjogXCJtYWM6V2luZG93RGlkRXhpdEZ1bGxTY3JlZW5cIixcclxuXHRcdFdpbmRvd0RpZEV4aXRWZXJzaW9uQnJvd3NlcjogXCJtYWM6V2luZG93RGlkRXhpdFZlcnNpb25Ccm93c2VyXCIsXHJcblx0XHRXaW5kb3dEaWRFeHBvc2U6IFwibWFjOldpbmRvd0RpZEV4cG9zZVwiLFxyXG5cdFx0V2luZG93RGlkRm9jdXM6IFwibWFjOldpbmRvd0RpZEZvY3VzXCIsXHJcblx0XHRXaW5kb3dEaWRNaW5pYXR1cml6ZTogXCJtYWM6V2luZG93RGlkTWluaWF0dXJpemVcIixcclxuXHRcdFdpbmRvd0RpZE1vdmU6IFwibWFjOldpbmRvd0RpZE1vdmVcIixcclxuXHRcdFdpbmRvd0RpZE9yZGVyT2ZmU2NyZWVuOiBcIm1hYzpXaW5kb3dEaWRPcmRlck9mZlNjcmVlblwiLFxyXG5cdFx0V2luZG93RGlkT3JkZXJPblNjcmVlbjogXCJtYWM6V2luZG93RGlkT3JkZXJPblNjcmVlblwiLFxyXG5cdFx0V2luZG93RGlkUmVzaWduS2V5OiBcIm1hYzpXaW5kb3dEaWRSZXNpZ25LZXlcIixcclxuXHRcdFdpbmRvd0RpZFJlc2lnbk1haW46IFwibWFjOldpbmRvd0RpZFJlc2lnbk1haW5cIixcclxuXHRcdFdpbmRvd0RpZFJlc2l6ZTogXCJtYWM6V2luZG93RGlkUmVzaXplXCIsXHJcblx0XHRXaW5kb3dEaWRVcGRhdGU6IFwibWFjOldpbmRvd0RpZFVwZGF0ZVwiLFxyXG5cdFx0V2luZG93RGlkVXBkYXRlQWxwaGE6IFwibWFjOldpbmRvd0RpZFVwZGF0ZUFscGhhXCIsXHJcblx0XHRXaW5kb3dEaWRVcGRhdGVDb2xsZWN0aW9uQmVoYXZpb3I6IFwibWFjOldpbmRvd0RpZFVwZGF0ZUNvbGxlY3Rpb25CZWhhdmlvclwiLFxyXG5cdFx0V2luZG93RGlkVXBkYXRlQ29sbGVjdGlvblByb3BlcnRpZXM6IFwibWFjOldpbmRvd0RpZFVwZGF0ZUNvbGxlY3Rpb25Qcm9wZXJ0aWVzXCIsXHJcblx0XHRXaW5kb3dEaWRVcGRhdGVTaGFkb3c6IFwibWFjOldpbmRvd0RpZFVwZGF0ZVNoYWRvd1wiLFxyXG5cdFx0V2luZG93RGlkVXBkYXRlVGl0bGU6IFwibWFjOldpbmRvd0RpZFVwZGF0ZVRpdGxlXCIsXHJcblx0XHRXaW5kb3dEaWRVcGRhdGVUb29sYmFyOiBcIm1hYzpXaW5kb3dEaWRVcGRhdGVUb29sYmFyXCIsXHJcblx0XHRXaW5kb3dEaWRab29tOiBcIm1hYzpXaW5kb3dEaWRab29tXCIsXHJcblx0XHRXaW5kb3dGaWxlRHJhZ2dpbmdFbnRlcmVkOiBcIm1hYzpXaW5kb3dGaWxlRHJhZ2dpbmdFbnRlcmVkXCIsXHJcblx0XHRXaW5kb3dGaWxlRHJhZ2dpbmdFeGl0ZWQ6IFwibWFjOldpbmRvd0ZpbGVEcmFnZ2luZ0V4aXRlZFwiLFxyXG5cdFx0V2luZG93RmlsZURyYWdnaW5nUGVyZm9ybWVkOiBcIm1hYzpXaW5kb3dGaWxlRHJhZ2dpbmdQZXJmb3JtZWRcIixcclxuXHRcdFdpbmRvd0hpZGU6IFwibWFjOldpbmRvd0hpZGVcIixcclxuXHRcdFdpbmRvd01heGltaXNlOiBcIm1hYzpXaW5kb3dNYXhpbWlzZVwiLFxyXG5cdFx0V2luZG93VW5NYXhpbWlzZTogXCJtYWM6V2luZG93VW5NYXhpbWlzZVwiLFxyXG5cdFx0V2luZG93TWluaW1pc2U6IFwibWFjOldpbmRvd01pbmltaXNlXCIsXHJcblx0XHRXaW5kb3dVbk1pbmltaXNlOiBcIm1hYzpXaW5kb3dVbk1pbmltaXNlXCIsXHJcblx0XHRXaW5kb3dTaG91bGRDbG9zZTogXCJtYWM6V2luZG93U2hvdWxkQ2xvc2VcIixcclxuXHRcdFdpbmRvd1Nob3c6IFwibWFjOldpbmRvd1Nob3dcIixcclxuXHRcdFdpbmRvd1dpbGxCZWNvbWVLZXk6IFwibWFjOldpbmRvd1dpbGxCZWNvbWVLZXlcIixcclxuXHRcdFdpbmRvd1dpbGxCZWNvbWVNYWluOiBcIm1hYzpXaW5kb3dXaWxsQmVjb21lTWFpblwiLFxyXG5cdFx0V2luZG93V2lsbEJlZ2luU2hlZXQ6IFwibWFjOldpbmRvd1dpbGxCZWdpblNoZWV0XCIsXHJcblx0XHRXaW5kb3dXaWxsQ2hhbmdlT3JkZXJpbmdNb2RlOiBcIm1hYzpXaW5kb3dXaWxsQ2hhbmdlT3JkZXJpbmdNb2RlXCIsXHJcblx0XHRXaW5kb3dXaWxsQ2xvc2U6IFwibWFjOldpbmRvd1dpbGxDbG9zZVwiLFxyXG5cdFx0V2luZG93V2lsbERlbWluaWF0dXJpemU6IFwibWFjOldpbmRvd1dpbGxEZW1pbmlhdHVyaXplXCIsXHJcblx0XHRXaW5kb3dXaWxsRW50ZXJGdWxsU2NyZWVuOiBcIm1hYzpXaW5kb3dXaWxsRW50ZXJGdWxsU2NyZWVuXCIsXHJcblx0XHRXaW5kb3dXaWxsRW50ZXJWZXJzaW9uQnJvd3NlcjogXCJtYWM6V2luZG93V2lsbEVudGVyVmVyc2lvbkJyb3dzZXJcIixcclxuXHRcdFdpbmRvd1dpbGxFeGl0RnVsbFNjcmVlbjogXCJtYWM6V2luZG93V2lsbEV4aXRGdWxsU2NyZWVuXCIsXHJcblx0XHRXaW5kb3dXaWxsRXhpdFZlcnNpb25Ccm93c2VyOiBcIm1hYzpXaW5kb3dXaWxsRXhpdFZlcnNpb25Ccm93c2VyXCIsXHJcblx0XHRXaW5kb3dXaWxsRm9jdXM6IFwibWFjOldpbmRvd1dpbGxGb2N1c1wiLFxyXG5cdFx0V2luZG93V2lsbE1pbmlhdHVyaXplOiBcIm1hYzpXaW5kb3dXaWxsTWluaWF0dXJpemVcIixcclxuXHRcdFdpbmRvd1dpbGxNb3ZlOiBcIm1hYzpXaW5kb3dXaWxsTW92ZVwiLFxyXG5cdFx0V2luZG93V2lsbE9yZGVyT2ZmU2NyZWVuOiBcIm1hYzpXaW5kb3dXaWxsT3JkZXJPZmZTY3JlZW5cIixcclxuXHRcdFdpbmRvd1dpbGxPcmRlck9uU2NyZWVuOiBcIm1hYzpXaW5kb3dXaWxsT3JkZXJPblNjcmVlblwiLFxyXG5cdFx0V2luZG93V2lsbFJlc2lnbk1haW46IFwibWFjOldpbmRvd1dpbGxSZXNpZ25NYWluXCIsXHJcblx0XHRXaW5kb3dXaWxsUmVzaXplOiBcIm1hYzpXaW5kb3dXaWxsUmVzaXplXCIsXHJcblx0XHRXaW5kb3dXaWxsVW5mb2N1czogXCJtYWM6V2luZG93V2lsbFVuZm9jdXNcIixcclxuXHRcdFdpbmRvd1dpbGxVcGRhdGU6IFwibWFjOldpbmRvd1dpbGxVcGRhdGVcIixcclxuXHRcdFdpbmRvd1dpbGxVcGRhdGVBbHBoYTogXCJtYWM6V2luZG93V2lsbFVwZGF0ZUFscGhhXCIsXHJcblx0XHRXaW5kb3dXaWxsVXBkYXRlQ29sbGVjdGlvbkJlaGF2aW9yOiBcIm1hYzpXaW5kb3dXaWxsVXBkYXRlQ29sbGVjdGlvbkJlaGF2aW9yXCIsXHJcblx0XHRXaW5kb3dXaWxsVXBkYXRlQ29sbGVjdGlvblByb3BlcnRpZXM6IFwibWFjOldpbmRvd1dpbGxVcGRhdGVDb2xsZWN0aW9uUHJvcGVydGllc1wiLFxyXG5cdFx0V2luZG93V2lsbFVwZGF0ZVNoYWRvdzogXCJtYWM6V2luZG93V2lsbFVwZGF0ZVNoYWRvd1wiLFxyXG5cdFx0V2luZG93V2lsbFVwZGF0ZVRpdGxlOiBcIm1hYzpXaW5kb3dXaWxsVXBkYXRlVGl0bGVcIixcclxuXHRcdFdpbmRvd1dpbGxVcGRhdGVUb29sYmFyOiBcIm1hYzpXaW5kb3dXaWxsVXBkYXRlVG9vbGJhclwiLFxyXG5cdFx0V2luZG93V2lsbFVwZGF0ZVZpc2liaWxpdHk6IFwibWFjOldpbmRvd1dpbGxVcGRhdGVWaXNpYmlsaXR5XCIsXHJcblx0XHRXaW5kb3dXaWxsVXNlU3RhbmRhcmRGcmFtZTogXCJtYWM6V2luZG93V2lsbFVzZVN0YW5kYXJkRnJhbWVcIixcclxuXHRcdFdpbmRvd1pvb21JbjogXCJtYWM6V2luZG93Wm9vbUluXCIsXHJcblx0XHRXaW5kb3dab29tT3V0OiBcIm1hYzpXaW5kb3dab29tT3V0XCIsXHJcblx0XHRXaW5kb3dab29tUmVzZXQ6IFwibWFjOldpbmRvd1pvb21SZXNldFwiLFxyXG5cdH0pLFxyXG5cdExpbnV4OiBPYmplY3QuZnJlZXplKHtcclxuXHRcdEFwcGxpY2F0aW9uU3RhcnR1cDogXCJsaW51eDpBcHBsaWNhdGlvblN0YXJ0dXBcIixcclxuXHRcdFN5c3RlbVRoZW1lQ2hhbmdlZDogXCJsaW51eDpTeXN0ZW1UaGVtZUNoYW5nZWRcIixcclxuXHRcdFdpbmRvd0RlbGV0ZUV2ZW50OiBcImxpbnV4OldpbmRvd0RlbGV0ZUV2ZW50XCIsXHJcblx0XHRXaW5kb3dEaWRNb3ZlOiBcImxpbnV4OldpbmRvd0RpZE1vdmVcIixcclxuXHRcdFdpbmRvd0RpZFJlc2l6ZTogXCJsaW51eDpXaW5kb3dEaWRSZXNpemVcIixcclxuXHRcdFdpbmRvd0ZvY3VzSW46IFwibGludXg6V2luZG93Rm9jdXNJblwiLFxyXG5cdFx0V2luZG93Rm9jdXNPdXQ6IFwibGludXg6V2luZG93Rm9jdXNPdXRcIixcclxuXHRcdFdpbmRvd0xvYWRDaGFuZ2VkOiBcImxpbnV4OldpbmRvd0xvYWRDaGFuZ2VkXCIsXHJcblx0fSksXHJcblx0Q29tbW9uOiBPYmplY3QuZnJlZXplKHtcclxuXHRcdEFwcGxpY2F0aW9uT3BlbmVkV2l0aEZpbGU6IFwiY29tbW9uOkFwcGxpY2F0aW9uT3BlbmVkV2l0aEZpbGVcIixcclxuXHRcdEFwcGxpY2F0aW9uU3RhcnRlZDogXCJjb21tb246QXBwbGljYXRpb25TdGFydGVkXCIsXHJcblx0XHRBcHBsaWNhdGlvbkxhdW5jaGVkV2l0aFVybDogXCJjb21tb246QXBwbGljYXRpb25MYXVuY2hlZFdpdGhVcmxcIixcclxuXHRcdFRoZW1lQ2hhbmdlZDogXCJjb21tb246VGhlbWVDaGFuZ2VkXCIsXHJcblx0XHRXaW5kb3dDbG9zaW5nOiBcImNvbW1vbjpXaW5kb3dDbG9zaW5nXCIsXHJcblx0XHRXaW5kb3dEaWRNb3ZlOiBcImNvbW1vbjpXaW5kb3dEaWRNb3ZlXCIsXHJcblx0XHRXaW5kb3dEaWRSZXNpemU6IFwiY29tbW9uOldpbmRvd0RpZFJlc2l6ZVwiLFxyXG5cdFx0V2luZG93RFBJQ2hhbmdlZDogXCJjb21tb246V2luZG93RFBJQ2hhbmdlZFwiLFxyXG5cdFx0V2luZG93RmlsZXNEcm9wcGVkOiBcImNvbW1vbjpXaW5kb3dGaWxlc0Ryb3BwZWRcIixcclxuXHRcdFdpbmRvd0ZvY3VzOiBcImNvbW1vbjpXaW5kb3dGb2N1c1wiLFxyXG5cdFx0V2luZG93RnVsbHNjcmVlbjogXCJjb21tb246V2luZG93RnVsbHNjcmVlblwiLFxyXG5cdFx0V2luZG93SGlkZTogXCJjb21tb246V2luZG93SGlkZVwiLFxyXG5cdFx0V2luZG93TG9zdEZvY3VzOiBcImNvbW1vbjpXaW5kb3dMb3N0Rm9jdXNcIixcclxuXHRcdFdpbmRvd01heGltaXNlOiBcImNvbW1vbjpXaW5kb3dNYXhpbWlzZVwiLFxyXG5cdFx0V2luZG93TWluaW1pc2U6IFwiY29tbW9uOldpbmRvd01pbmltaXNlXCIsXHJcblx0XHRXaW5kb3dUb2dnbGVGcmFtZWxlc3M6IFwiY29tbW9uOldpbmRvd1RvZ2dsZUZyYW1lbGVzc1wiLFxyXG5cdFx0V2luZG93UmVzdG9yZTogXCJjb21tb246V2luZG93UmVzdG9yZVwiLFxyXG5cdFx0V2luZG93UnVudGltZVJlYWR5OiBcImNvbW1vbjpXaW5kb3dSdW50aW1lUmVhZHlcIixcclxuXHRcdFdpbmRvd1Nob3c6IFwiY29tbW9uOldpbmRvd1Nob3dcIixcclxuXHRcdFdpbmRvd1VuRnVsbHNjcmVlbjogXCJjb21tb246V2luZG93VW5GdWxsc2NyZWVuXCIsXHJcblx0XHRXaW5kb3dVbk1heGltaXNlOiBcImNvbW1vbjpXaW5kb3dVbk1heGltaXNlXCIsXHJcblx0XHRXaW5kb3dVbk1pbmltaXNlOiBcImNvbW1vbjpXaW5kb3dVbk1pbmltaXNlXCIsXHJcblx0XHRXaW5kb3dab29tOiBcImNvbW1vbjpXaW5kb3dab29tXCIsXHJcblx0XHRXaW5kb3dab29tSW46IFwiY29tbW9uOldpbmRvd1pvb21JblwiLFxyXG5cdFx0V2luZG93Wm9vbU91dDogXCJjb21tb246V2luZG93Wm9vbU91dFwiLFxyXG5cdFx0V2luZG93Wm9vbVJlc2V0OiBcImNvbW1vbjpXaW5kb3dab29tUmVzZXRcIixcclxuXHRcdFdpbmRvd0Ryb3Bab25lRmlsZXNEcm9wcGVkOiBcImNvbW1vbjpXaW5kb3dEcm9wWm9uZUZpbGVzRHJvcHBlZFwiLFxyXG5cdH0pLFxyXG59KTtcclxuIiwgIi8qXHJcbiBfICAgICBfXyAgICAgXyBfX1xyXG58IHwgIC8gL19fXyBfKF8pIC9fX19fXHJcbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cclxufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXHJcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xyXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXHJcbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcclxuKi9cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB0byB0aGUgY29uc29sZSB3aXRoIGN1c3RvbSBmb3JtYXR0aW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0gbWVzc2FnZSAtIFRoZSBtZXNzYWdlIHRvIGJlIGxvZ2dlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWJ1Z0xvZyhtZXNzYWdlOiBhbnkpIHtcclxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZVxyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgJyVjIHdhaWxzMyAlYyAnICsgbWVzc2FnZSArICcgJyxcclxuICAgICAgICAnYmFja2dyb3VuZDogI2FhMDAwMDsgY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDNweCAwcHggMHB4IDNweDsgcGFkZGluZzogMXB4OyBmb250LXNpemU6IDAuN3JlbScsXHJcbiAgICAgICAgJ2JhY2tncm91bmQ6ICMwMDk5MDA7IGNvbG9yOiAjZmZmOyBib3JkZXItcmFkaXVzOiAwcHggM3B4IDNweCAwcHg7IHBhZGRpbmc6IDFweDsgZm9udC1zaXplOiAwLjdyZW0nXHJcbiAgICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHdoZXRoZXIgdGhlIHdlYnZpZXcgc3VwcG9ydHMgdGhlIHtAbGluayBNb3VzZUV2ZW50I2J1dHRvbnN9IHByb3BlcnR5LlxyXG4gKiBMb29raW5nIGF0IHlvdSBtYWNPUyBIaWdoIFNpZXJyYSFcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjYW5UcmFja0J1dHRvbnMoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gKG5ldyBNb3VzZUV2ZW50KCdtb3VzZWRvd24nKSkuYnV0dG9ucyA9PT0gMDtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB3aGV0aGVyIHRoZSBicm93c2VyIHN1cHBvcnRzIHJlbW92aW5nIGxpc3RlbmVycyBieSB0cmlnZ2VyaW5nIGFuIEFib3J0U2lnbmFsXHJcbiAqIChzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0V2ZW50VGFyZ2V0L2FkZEV2ZW50TGlzdGVuZXIjc2lnbmFsKS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjYW5BYm9ydExpc3RlbmVycygpIHtcclxuICAgIGlmICghRXZlbnRUYXJnZXQgfHwgIUFib3J0U2lnbmFsIHx8ICFBYm9ydENvbnRyb2xsZXIpXHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG5cclxuICAgIGxldCByZXN1bHQgPSB0cnVlO1xyXG5cclxuICAgIGNvbnN0IHRhcmdldCA9IG5ldyBFdmVudFRhcmdldCgpO1xyXG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcclxuICAgIHRhcmdldC5hZGRFdmVudExpc3RlbmVyKCd0ZXN0JywgKCkgPT4geyByZXN1bHQgPSBmYWxzZTsgfSwgeyBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsIH0pO1xyXG4gICAgY29udHJvbGxlci5hYm9ydCgpO1xyXG4gICAgdGFyZ2V0LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCd0ZXN0JykpO1xyXG5cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXNvbHZlcyB0aGUgY2xvc2VzdCBIVE1MRWxlbWVudCBhbmNlc3RvciBvZiBhbiBldmVudCdzIHRhcmdldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBldmVudFRhcmdldChldmVudDogRXZlbnQpOiBIVE1MRWxlbWVudCB7XHJcbiAgICBpZiAoZXZlbnQudGFyZ2V0IGluc3RhbmNlb2YgSFRNTEVsZW1lbnQpIHtcclxuICAgICAgICByZXR1cm4gZXZlbnQudGFyZ2V0O1xyXG4gICAgfSBlbHNlIGlmICghKGV2ZW50LnRhcmdldCBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSAmJiBldmVudC50YXJnZXQgaW5zdGFuY2VvZiBOb2RlKSB7XHJcbiAgICAgICAgcmV0dXJuIGV2ZW50LnRhcmdldC5wYXJlbnRFbGVtZW50ID8/IGRvY3VtZW50LmJvZHk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAgIHJldHVybiBkb2N1bWVudC5ib2R5O1xyXG4gICAgfVxyXG59XHJcblxyXG4vKioqXHJcbiBUaGlzIHRlY2huaXF1ZSBmb3IgcHJvcGVyIGxvYWQgZGV0ZWN0aW9uIGlzIHRha2VuIGZyb20gSFRNWDpcclxuXHJcbiBCU0QgMi1DbGF1c2UgTGljZW5zZVxyXG5cclxuIENvcHlyaWdodCAoYykgMjAyMCwgQmlnIFNreSBTb2Z0d2FyZVxyXG4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuXHJcbiBSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXRcclxuIG1vZGlmaWNhdGlvbiwgYXJlIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucyBhcmUgbWV0OlxyXG5cclxuIDEuIFJlZGlzdHJpYnV0aW9ucyBvZiBzb3VyY2UgY29kZSBtdXN0IHJldGFpbiB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSwgdGhpc1xyXG4gbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuXHJcblxyXG4gMi4gUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3JtIG11c3QgcmVwcm9kdWNlIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLFxyXG4gdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUgZG9jdW1lbnRhdGlvblxyXG4gYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uXHJcblxyXG4gVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUyBcIkFTIElTXCJcclxuIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBUSEVcclxuIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFSRVxyXG4gRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRVxyXG4gRk9SIEFOWSBESVJFQ1QsIElORElSRUNULCBJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUxcclxuIERBTUFHRVMgKElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBQUk9DVVJFTUVOVCBPRiBTVUJTVElUVVRFIEdPT0RTIE9SXHJcbiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUlxyXG4gQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUIExJQUJJTElUWSxcclxuIE9SIFRPUlQgKElOQ0xVRElORyBORUdMSUdFTkNFIE9SIE9USEVSV0lTRSkgQVJJU0lORyBJTiBBTlkgV0FZIE9VVCBPRiBUSEUgVVNFXHJcbiBPRiBUSElTIFNPRlRXQVJFLCBFVkVOIElGIEFEVklTRUQgT0YgVEhFIFBPU1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFLlxyXG5cclxuICoqKi9cclxuXHJcbmxldCBpc1JlYWR5ID0gZmFsc2U7XHJcbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7IGlzUmVhZHkgPSB0cnVlIH0pO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHdoZW5SZWFkeShjYWxsYmFjazogKCkgPT4gdm9pZCkge1xyXG4gICAgaWYgKGlzUmVhZHkgfHwgZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2NvbXBsZXRlJykge1xyXG4gICAgICAgIGNhbGxiYWNrKCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBjYWxsYmFjayk7XHJcbiAgICB9XHJcbn1cclxuIiwgIi8qXHJcbiBfXHQgICBfX1x0ICBfIF9fXHJcbnwgfFx0IC8gL19fXyBfKF8pIC9fX19fXHJcbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cclxufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXHJcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xyXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXHJcbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcclxuKi9cclxuXHJcbmltcG9ydCB7bmV3UnVudGltZUNhbGxlciwgb2JqZWN0TmFtZXN9IGZyb20gXCIuL3J1bnRpbWUuanNcIjtcclxuaW1wb3J0IHR5cGUgeyBTY3JlZW4gfSBmcm9tIFwiLi9zY3JlZW5zLmpzXCI7XHJcblxyXG4vLyBORVc6IERyb3B6b25lIGNvbnN0YW50c1xyXG5jb25zdCBEUk9QWk9ORV9BVFRSSUJVVEUgPSAnZGF0YS13YWlscy1kcm9wem9uZSc7XHJcbmNvbnN0IERST1BaT05FX0hPVkVSX0NMQVNTID0gJ3dhaWxzLWRyb3B6b25lLWhvdmVyJzsgLy8gVXNlciBjYW4gc3R5bGUgdGhpcyBjbGFzc1xyXG5sZXQgY3VycmVudEhvdmVyZWREcm9wem9uZTogRWxlbWVudCB8IG51bGwgPSBudWxsO1xyXG5cclxuY29uc3QgUG9zaXRpb25NZXRob2QgICAgICAgICAgICAgICAgICAgID0gMDtcclxuY29uc3QgQ2VudGVyTWV0aG9kICAgICAgICAgICAgICAgICAgICAgID0gMTtcclxuY29uc3QgQ2xvc2VNZXRob2QgICAgICAgICAgICAgICAgICAgICAgID0gMjtcclxuY29uc3QgRGlzYWJsZVNpemVDb25zdHJhaW50c01ldGhvZCAgICAgID0gMztcclxuY29uc3QgRW5hYmxlU2l6ZUNvbnN0cmFpbnRzTWV0aG9kICAgICAgID0gNDtcclxuY29uc3QgRm9jdXNNZXRob2QgICAgICAgICAgICAgICAgICAgICAgID0gNTtcclxuY29uc3QgRm9yY2VSZWxvYWRNZXRob2QgICAgICAgICAgICAgICAgID0gNjtcclxuY29uc3QgRnVsbHNjcmVlbk1ldGhvZCAgICAgICAgICAgICAgICAgID0gNztcclxuY29uc3QgR2V0U2NyZWVuTWV0aG9kICAgICAgICAgICAgICAgICAgID0gODtcclxuY29uc3QgR2V0Wm9vbU1ldGhvZCAgICAgICAgICAgICAgICAgICAgID0gOTtcclxuY29uc3QgSGVpZ2h0TWV0aG9kICAgICAgICAgICAgICAgICAgICAgID0gMTA7XHJcbmNvbnN0IEhpZGVNZXRob2QgICAgICAgICAgICAgICAgICAgICAgICA9IDExO1xyXG5jb25zdCBJc0ZvY3VzZWRNZXRob2QgICAgICAgICAgICAgICAgICAgPSAxMjtcclxuY29uc3QgSXNGdWxsc2NyZWVuTWV0aG9kICAgICAgICAgICAgICAgID0gMTM7XHJcbmNvbnN0IElzTWF4aW1pc2VkTWV0aG9kICAgICAgICAgICAgICAgICA9IDE0O1xyXG5jb25zdCBJc01pbmltaXNlZE1ldGhvZCAgICAgICAgICAgICAgICAgPSAxNTtcclxuY29uc3QgTWF4aW1pc2VNZXRob2QgICAgICAgICAgICAgICAgICAgID0gMTY7XHJcbmNvbnN0IE1pbmltaXNlTWV0aG9kICAgICAgICAgICAgICAgICAgICA9IDE3O1xyXG5jb25zdCBOYW1lTWV0aG9kICAgICAgICAgICAgICAgICAgICAgICAgPSAxODtcclxuY29uc3QgT3BlbkRldlRvb2xzTWV0aG9kICAgICAgICAgICAgICAgID0gMTk7XHJcbmNvbnN0IFJlbGF0aXZlUG9zaXRpb25NZXRob2QgICAgICAgICAgICA9IDIwO1xyXG5jb25zdCBSZWxvYWRNZXRob2QgICAgICAgICAgICAgICAgICAgICAgPSAyMTtcclxuY29uc3QgUmVzaXphYmxlTWV0aG9kICAgICAgICAgICAgICAgICAgID0gMjI7XHJcbmNvbnN0IFJlc3RvcmVNZXRob2QgICAgICAgICAgICAgICAgICAgICA9IDIzO1xyXG5jb25zdCBTZXRQb3NpdGlvbk1ldGhvZCAgICAgICAgICAgICAgICAgPSAyNDtcclxuY29uc3QgU2V0QWx3YXlzT25Ub3BNZXRob2QgICAgICAgICAgICAgID0gMjU7XHJcbmNvbnN0IFNldEJhY2tncm91bmRDb2xvdXJNZXRob2QgICAgICAgICA9IDI2O1xyXG5jb25zdCBTZXRGcmFtZWxlc3NNZXRob2QgICAgICAgICAgICAgICAgPSAyNztcclxuY29uc3QgU2V0RnVsbHNjcmVlbkJ1dHRvbkVuYWJsZWRNZXRob2QgID0gMjg7XHJcbmNvbnN0IFNldE1heFNpemVNZXRob2QgICAgICAgICAgICAgICAgICA9IDI5O1xyXG5jb25zdCBTZXRNaW5TaXplTWV0aG9kICAgICAgICAgICAgICAgICAgPSAzMDtcclxuY29uc3QgU2V0UmVsYXRpdmVQb3NpdGlvbk1ldGhvZCAgICAgICAgID0gMzE7XHJcbmNvbnN0IFNldFJlc2l6YWJsZU1ldGhvZCAgICAgICAgICAgICAgICA9IDMyO1xyXG5jb25zdCBTZXRTaXplTWV0aG9kICAgICAgICAgICAgICAgICAgICAgPSAzMztcclxuY29uc3QgU2V0VGl0bGVNZXRob2QgICAgICAgICAgICAgICAgICAgID0gMzQ7XHJcbmNvbnN0IFNldFpvb21NZXRob2QgICAgICAgICAgICAgICAgICAgICA9IDM1O1xyXG5jb25zdCBTaG93TWV0aG9kICAgICAgICAgICAgICAgICAgICAgICAgPSAzNjtcclxuY29uc3QgU2l6ZU1ldGhvZCAgICAgICAgICAgICAgICAgICAgICAgID0gMzc7XHJcbmNvbnN0IFRvZ2dsZUZ1bGxzY3JlZW5NZXRob2QgICAgICAgICAgICA9IDM4O1xyXG5jb25zdCBUb2dnbGVNYXhpbWlzZU1ldGhvZCAgICAgICAgICAgICAgPSAzOTtcclxuY29uc3QgVG9nZ2xlRnJhbWVsZXNzTWV0aG9kICAgICAgICAgICAgID0gNDA7IFxyXG5jb25zdCBVbkZ1bGxzY3JlZW5NZXRob2QgICAgICAgICAgICAgICAgPSA0MTtcclxuY29uc3QgVW5NYXhpbWlzZU1ldGhvZCAgICAgICAgICAgICAgICAgID0gNDI7XHJcbmNvbnN0IFVuTWluaW1pc2VNZXRob2QgICAgICAgICAgICAgICAgICA9IDQzO1xyXG5jb25zdCBXaWR0aE1ldGhvZCAgICAgICAgICAgICAgICAgICAgICAgPSA0NDtcclxuY29uc3QgWm9vbU1ldGhvZCAgICAgICAgICAgICAgICAgICAgICAgID0gNDU7XHJcbmNvbnN0IFpvb21Jbk1ldGhvZCAgICAgICAgICAgICAgICAgICAgICA9IDQ2O1xyXG5jb25zdCBab29tT3V0TWV0aG9kICAgICAgICAgICAgICAgICAgICAgPSA0NztcclxuY29uc3QgWm9vbVJlc2V0TWV0aG9kICAgICAgICAgICAgICAgICAgID0gNDg7XHJcbmNvbnN0IFdpbmRvd0Ryb3Bab25lRHJvcHBlZCAgICAgICAgICAgICA9IDQ5O1xyXG5cclxuZnVuY3Rpb24gZ2V0RHJvcHpvbmVFbGVtZW50KGVsZW1lbnQ6IEVsZW1lbnQgfCBudWxsKTogRWxlbWVudCB8IG51bGwge1xyXG4gICAgaWYgKCFlbGVtZW50KSB7XHJcbiAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgICAvLyBBbGxvdyBkcm9wem9uZSBhdHRyaWJ1dGUgdG8gYmUgb24gdGhlIGVsZW1lbnQgaXRzZWxmIG9yIGFueSBwYXJlbnRcclxuICAgIHJldHVybiBlbGVtZW50LmNsb3Nlc3QoYFske0RST1BaT05FX0FUVFJJQlVURX1dYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBIHJlY29yZCBkZXNjcmliaW5nIHRoZSBwb3NpdGlvbiBvZiBhIHdpbmRvdy5cclxuICovXHJcbmludGVyZmFjZSBQb3NpdGlvbiB7XHJcbiAgICAvKiogVGhlIGhvcml6b250YWwgcG9zaXRpb24gb2YgdGhlIHdpbmRvdy4gKi9cclxuICAgIHg6IG51bWJlcjtcclxuICAgIC8qKiBUaGUgdmVydGljYWwgcG9zaXRpb24gb2YgdGhlIHdpbmRvdy4gKi9cclxuICAgIHk6IG51bWJlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIEEgcmVjb3JkIGRlc2NyaWJpbmcgdGhlIHNpemUgb2YgYSB3aW5kb3cuXHJcbiAqL1xyXG5pbnRlcmZhY2UgU2l6ZSB7XHJcbiAgICAvKiogVGhlIHdpZHRoIG9mIHRoZSB3aW5kb3cuICovXHJcbiAgICB3aWR0aDogbnVtYmVyO1xyXG4gICAgLyoqIFRoZSBoZWlnaHQgb2YgdGhlIHdpbmRvdy4gKi9cclxuICAgIGhlaWdodDogbnVtYmVyO1xyXG59XHJcblxyXG4vLyBQcml2YXRlIGZpZWxkIG5hbWVzLlxyXG5jb25zdCBjYWxsZXJTeW0gPSBTeW1ib2woXCJjYWxsZXJcIik7XHJcblxyXG5jbGFzcyBXaW5kb3cge1xyXG4gICAgLy8gUHJpdmF0ZSBmaWVsZHMuXHJcbiAgICBwcml2YXRlIFtjYWxsZXJTeW1dOiAobWVzc2FnZTogbnVtYmVyLCBhcmdzPzogYW55KSA9PiBQcm9taXNlPGFueT47XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbml0aWFsaXNlcyBhIHdpbmRvdyBvYmplY3Qgd2l0aCB0aGUgc3BlY2lmaWVkIG5hbWUuXHJcbiAgICAgKlxyXG4gICAgICogQHByaXZhdGVcclxuICAgICAqIEBwYXJhbSBuYW1lIC0gVGhlIG5hbWUgb2YgdGhlIHRhcmdldCB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIGNvbnN0cnVjdG9yKG5hbWU6IHN0cmluZyA9ICcnKSB7XHJcbiAgICAgICAgdGhpc1tjYWxsZXJTeW1dID0gbmV3UnVudGltZUNhbGxlcihvYmplY3ROYW1lcy5XaW5kb3csIG5hbWUpXHJcblxyXG4gICAgICAgIC8vIGJpbmQgaW5zdGFuY2UgbWV0aG9kIHRvIG1ha2UgdGhlbSBlYXNpbHkgdXNhYmxlIGluIGV2ZW50IGhhbmRsZXJzXHJcbiAgICAgICAgZm9yIChjb25zdCBtZXRob2Qgb2YgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMoV2luZG93LnByb3RvdHlwZSkpIHtcclxuICAgICAgICAgICAgaWYgKFxyXG4gICAgICAgICAgICAgICAgbWV0aG9kICE9PSBcImNvbnN0cnVjdG9yXCJcclxuICAgICAgICAgICAgICAgICYmIHR5cGVvZiAodGhpcyBhcyBhbnkpW21ldGhvZF0gPT09IFwiZnVuY3Rpb25cIlxyXG4gICAgICAgICAgICApIHtcclxuICAgICAgICAgICAgICAgICh0aGlzIGFzIGFueSlbbWV0aG9kXSA9ICh0aGlzIGFzIGFueSlbbWV0aG9kXS5iaW5kKHRoaXMpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogR2V0cyB0aGUgc3BlY2lmaWVkIHdpbmRvdy5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gbmFtZSAtIFRoZSBuYW1lIG9mIHRoZSB3aW5kb3cgdG8gZ2V0LlxyXG4gICAgICogQHJldHVybnMgVGhlIGNvcnJlc3BvbmRpbmcgd2luZG93IG9iamVjdC5cclxuICAgICAqL1xyXG4gICAgR2V0KG5hbWU6IHN0cmluZyk6IFdpbmRvdyB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBXaW5kb3cobmFtZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXR1cm5zIHRoZSBhYnNvbHV0ZSBwb3NpdGlvbiBvZiB0aGUgd2luZG93LlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFRoZSBjdXJyZW50IGFic29sdXRlIHBvc2l0aW9uIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFBvc2l0aW9uKCk6IFByb21pc2U8UG9zaXRpb24+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFBvc2l0aW9uTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIENlbnRlcnMgdGhlIHdpbmRvdyBvbiB0aGUgc2NyZWVuLlxyXG4gICAgICovXHJcbiAgICBDZW50ZXIoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShDZW50ZXJNZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQ2xvc2VzIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIENsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oQ2xvc2VNZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGlzYWJsZXMgbWluL21heCBzaXplIGNvbnN0cmFpbnRzLlxyXG4gICAgICovXHJcbiAgICBEaXNhYmxlU2l6ZUNvbnN0cmFpbnRzKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oRGlzYWJsZVNpemVDb25zdHJhaW50c01ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBFbmFibGVzIG1pbi9tYXggc2l6ZSBjb25zdHJhaW50cy5cclxuICAgICAqL1xyXG4gICAgRW5hYmxlU2l6ZUNvbnN0cmFpbnRzKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oRW5hYmxlU2l6ZUNvbnN0cmFpbnRzTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEZvY3VzZXMgdGhlIHdpbmRvdy5cclxuICAgICAqL1xyXG4gICAgRm9jdXMoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShGb2N1c01ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBGb3JjZXMgdGhlIHdpbmRvdyB0byByZWxvYWQgdGhlIHBhZ2UgYXNzZXRzLlxyXG4gICAgICovXHJcbiAgICBGb3JjZVJlbG9hZCgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKEZvcmNlUmVsb2FkTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFN3aXRjaGVzIHRoZSB3aW5kb3cgdG8gZnVsbHNjcmVlbiBtb2RlLlxyXG4gICAgICovXHJcbiAgICBGdWxsc2NyZWVuKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oRnVsbHNjcmVlbk1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXR1cm5zIHRoZSBzY3JlZW4gdGhhdCB0aGUgd2luZG93IGlzIG9uLlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFRoZSBzY3JlZW4gdGhlIHdpbmRvdyBpcyBjdXJyZW50bHkgb24uXHJcbiAgICAgKi9cclxuICAgIEdldFNjcmVlbigpOiBQcm9taXNlPFNjcmVlbj4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oR2V0U2NyZWVuTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJldHVybnMgdGhlIGN1cnJlbnQgem9vbSBsZXZlbCBvZiB0aGUgd2luZG93LlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFRoZSBjdXJyZW50IHpvb20gbGV2ZWwuXHJcbiAgICAgKi9cclxuICAgIEdldFpvb20oKTogUHJvbWlzZTxudW1iZXI+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKEdldFpvb21NZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogUmV0dXJucyB0aGUgaGVpZ2h0IG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgVGhlIGN1cnJlbnQgaGVpZ2h0IG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIEhlaWdodCgpOiBQcm9taXNlPG51bWJlcj4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oSGVpZ2h0TWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEhpZGVzIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIEhpZGUoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShIaWRlTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgd2luZG93IGlzIGZvY3VzZWQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgV2hldGhlciB0aGUgd2luZG93IGlzIGN1cnJlbnRseSBmb2N1c2VkLlxyXG4gICAgICovXHJcbiAgICBJc0ZvY3VzZWQoKTogUHJvbWlzZTxib29sZWFuPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShJc0ZvY3VzZWRNZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogUmV0dXJucyB0cnVlIGlmIHRoZSB3aW5kb3cgaXMgZnVsbHNjcmVlbi5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBXaGV0aGVyIHRoZSB3aW5kb3cgaXMgY3VycmVudGx5IGZ1bGxzY3JlZW4uXHJcbiAgICAgKi9cclxuICAgIElzRnVsbHNjcmVlbigpOiBQcm9taXNlPGJvb2xlYW4+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKElzRnVsbHNjcmVlbk1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXR1cm5zIHRydWUgaWYgdGhlIHdpbmRvdyBpcyBtYXhpbWlzZWQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgV2hldGhlciB0aGUgd2luZG93IGlzIGN1cnJlbnRseSBtYXhpbWlzZWQuXHJcbiAgICAgKi9cclxuICAgIElzTWF4aW1pc2VkKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oSXNNYXhpbWlzZWRNZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogUmV0dXJucyB0cnVlIGlmIHRoZSB3aW5kb3cgaXMgbWluaW1pc2VkLlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFdoZXRoZXIgdGhlIHdpbmRvdyBpcyBjdXJyZW50bHkgbWluaW1pc2VkLlxyXG4gICAgICovXHJcbiAgICBJc01pbmltaXNlZCgpOiBQcm9taXNlPGJvb2xlYW4+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKElzTWluaW1pc2VkTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIE1heGltaXNlcyB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBNYXhpbWlzZSgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKE1heGltaXNlTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIE1pbmltaXNlcyB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBNaW5pbWlzZSgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKE1pbmltaXNlTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJldHVybnMgdGhlIG5hbWUgb2YgdGhlIHdpbmRvdy5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBUaGUgbmFtZSBvZiB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBOYW1lKCk6IFByb21pc2U8c3RyaW5nPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShOYW1lTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIE9wZW5zIHRoZSBkZXZlbG9wbWVudCB0b29scyBwYW5lLlxyXG4gICAgICovXHJcbiAgICBPcGVuRGV2VG9vbHMoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShPcGVuRGV2VG9vbHNNZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogUmV0dXJucyB0aGUgcmVsYXRpdmUgcG9zaXRpb24gb2YgdGhlIHdpbmRvdyB0byB0aGUgc2NyZWVuLlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFRoZSBjdXJyZW50IHJlbGF0aXZlIHBvc2l0aW9uIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFJlbGF0aXZlUG9zaXRpb24oKTogUHJvbWlzZTxQb3NpdGlvbj4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oUmVsYXRpdmVQb3NpdGlvbk1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZWxvYWRzIHRoZSBwYWdlIGFzc2V0cy5cclxuICAgICAqL1xyXG4gICAgUmVsb2FkKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oUmVsb2FkTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgd2luZG93IGlzIHJlc2l6YWJsZS5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBXaGV0aGVyIHRoZSB3aW5kb3cgaXMgY3VycmVudGx5IHJlc2l6YWJsZS5cclxuICAgICAqL1xyXG4gICAgUmVzaXphYmxlKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oUmVzaXphYmxlTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJlc3RvcmVzIHRoZSB3aW5kb3cgdG8gaXRzIHByZXZpb3VzIHN0YXRlIGlmIGl0IHdhcyBwcmV2aW91c2x5IG1pbmltaXNlZCwgbWF4aW1pc2VkIG9yIGZ1bGxzY3JlZW4uXHJcbiAgICAgKi9cclxuICAgIFJlc3RvcmUoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShSZXN0b3JlTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFNldHMgdGhlIGFic29sdXRlIHBvc2l0aW9uIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHggLSBUaGUgZGVzaXJlZCBob3Jpem9udGFsIGFic29sdXRlIHBvc2l0aW9uIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKiBAcGFyYW0geSAtIFRoZSBkZXNpcmVkIHZlcnRpY2FsIGFic29sdXRlIHBvc2l0aW9uIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFNldFBvc2l0aW9uKHg6IG51bWJlciwgeTogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShTZXRQb3NpdGlvbk1ldGhvZCwgeyB4LCB5IH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogU2V0cyB0aGUgd2luZG93IHRvIGJlIGFsd2F5cyBvbiB0b3AuXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIGFsd2F5c09uVG9wIC0gV2hldGhlciB0aGUgd2luZG93IHNob3VsZCBzdGF5IG9uIHRvcC5cclxuICAgICAqL1xyXG4gICAgU2V0QWx3YXlzT25Ub3AoYWx3YXlzT25Ub3A6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFNldEFsd2F5c09uVG9wTWV0aG9kLCB7IGFsd2F5c09uVG9wIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogU2V0cyB0aGUgYmFja2dyb3VuZCBjb2xvdXIgb2YgdGhlIHdpbmRvdy5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gciAtIFRoZSBkZXNpcmVkIHJlZCBjb21wb25lbnQgb2YgdGhlIHdpbmRvdyBiYWNrZ3JvdW5kLlxyXG4gICAgICogQHBhcmFtIGcgLSBUaGUgZGVzaXJlZCBncmVlbiBjb21wb25lbnQgb2YgdGhlIHdpbmRvdyBiYWNrZ3JvdW5kLlxyXG4gICAgICogQHBhcmFtIGIgLSBUaGUgZGVzaXJlZCBibHVlIGNvbXBvbmVudCBvZiB0aGUgd2luZG93IGJhY2tncm91bmQuXHJcbiAgICAgKiBAcGFyYW0gYSAtIFRoZSBkZXNpcmVkIGFscGhhIGNvbXBvbmVudCBvZiB0aGUgd2luZG93IGJhY2tncm91bmQuXHJcbiAgICAgKi9cclxuICAgIFNldEJhY2tncm91bmRDb2xvdXIocjogbnVtYmVyLCBnOiBudW1iZXIsIGI6IG51bWJlciwgYTogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShTZXRCYWNrZ3JvdW5kQ29sb3VyTWV0aG9kLCB7IHIsIGcsIGIsIGEgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZW1vdmVzIHRoZSB3aW5kb3cgZnJhbWUgYW5kIHRpdGxlIGJhci5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gZnJhbWVsZXNzIC0gV2hldGhlciB0aGUgd2luZG93IHNob3VsZCBiZSBmcmFtZWxlc3MuXHJcbiAgICAgKi9cclxuICAgIFNldEZyYW1lbGVzcyhmcmFtZWxlc3M6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFNldEZyYW1lbGVzc01ldGhvZCwgeyBmcmFtZWxlc3MgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBEaXNhYmxlcyB0aGUgc3lzdGVtIGZ1bGxzY3JlZW4gYnV0dG9uLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBlbmFibGVkIC0gV2hldGhlciB0aGUgZnVsbHNjcmVlbiBidXR0b24gc2hvdWxkIGJlIGVuYWJsZWQuXHJcbiAgICAgKi9cclxuICAgIFNldEZ1bGxzY3JlZW5CdXR0b25FbmFibGVkKGVuYWJsZWQ6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFNldEZ1bGxzY3JlZW5CdXR0b25FbmFibGVkTWV0aG9kLCB7IGVuYWJsZWQgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBTZXRzIHRoZSBtYXhpbXVtIHNpemUgb2YgdGhlIHdpbmRvdy5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gd2lkdGggLSBUaGUgZGVzaXJlZCBtYXhpbXVtIHdpZHRoIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKiBAcGFyYW0gaGVpZ2h0IC0gVGhlIGRlc2lyZWQgbWF4aW11bSBoZWlnaHQgb2YgdGhlIHdpbmRvdy5cclxuICAgICAqL1xyXG4gICAgU2V0TWF4U2l6ZSh3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oU2V0TWF4U2l6ZU1ldGhvZCwgeyB3aWR0aCwgaGVpZ2h0IH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogU2V0cyB0aGUgbWluaW11bSBzaXplIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHdpZHRoIC0gVGhlIGRlc2lyZWQgbWluaW11bSB3aWR0aCBvZiB0aGUgd2luZG93LlxyXG4gICAgICogQHBhcmFtIGhlaWdodCAtIFRoZSBkZXNpcmVkIG1pbmltdW0gaGVpZ2h0IG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFNldE1pblNpemUod2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFNldE1pblNpemVNZXRob2QsIHsgd2lkdGgsIGhlaWdodCB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFNldHMgdGhlIHJlbGF0aXZlIHBvc2l0aW9uIG9mIHRoZSB3aW5kb3cgdG8gdGhlIHNjcmVlbi5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0geCAtIFRoZSBkZXNpcmVkIGhvcml6b250YWwgcmVsYXRpdmUgcG9zaXRpb24gb2YgdGhlIHdpbmRvdy5cclxuICAgICAqIEBwYXJhbSB5IC0gVGhlIGRlc2lyZWQgdmVydGljYWwgcmVsYXRpdmUgcG9zaXRpb24gb2YgdGhlIHdpbmRvdy5cclxuICAgICAqL1xyXG4gICAgU2V0UmVsYXRpdmVQb3NpdGlvbih4OiBudW1iZXIsIHk6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oU2V0UmVsYXRpdmVQb3NpdGlvbk1ldGhvZCwgeyB4LCB5IH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogU2V0cyB3aGV0aGVyIHRoZSB3aW5kb3cgaXMgcmVzaXphYmxlLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSByZXNpemFibGUgLSBXaGV0aGVyIHRoZSB3aW5kb3cgc2hvdWxkIGJlIHJlc2l6YWJsZS5cclxuICAgICAqL1xyXG4gICAgU2V0UmVzaXphYmxlKHJlc2l6YWJsZTogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oU2V0UmVzaXphYmxlTWV0aG9kLCB7IHJlc2l6YWJsZSB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFNldHMgdGhlIHNpemUgb2YgdGhlIHdpbmRvdy5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gd2lkdGggLSBUaGUgZGVzaXJlZCB3aWR0aCBvZiB0aGUgd2luZG93LlxyXG4gICAgICogQHBhcmFtIGhlaWdodCAtIFRoZSBkZXNpcmVkIGhlaWdodCBvZiB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBTZXRTaXplKHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShTZXRTaXplTWV0aG9kLCB7IHdpZHRoLCBoZWlnaHQgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBTZXRzIHRoZSB0aXRsZSBvZiB0aGUgd2luZG93LlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB0aXRsZSAtIFRoZSBkZXNpcmVkIHRpdGxlIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFNldFRpdGxlKHRpdGxlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFNldFRpdGxlTWV0aG9kLCB7IHRpdGxlIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogU2V0cyB0aGUgem9vbSBsZXZlbCBvZiB0aGUgd2luZG93LlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB6b29tIC0gVGhlIGRlc2lyZWQgem9vbSBsZXZlbC5cclxuICAgICAqL1xyXG4gICAgU2V0Wm9vbSh6b29tOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFNldFpvb21NZXRob2QsIHsgem9vbSB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFNob3dzIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFNob3coKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShTaG93TWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFJldHVybnMgdGhlIHNpemUgb2YgdGhlIHdpbmRvdy5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBUaGUgY3VycmVudCBzaXplIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFNpemUoKTogUHJvbWlzZTxTaXplPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShTaXplTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFRvZ2dsZXMgdGhlIHdpbmRvdyBiZXR3ZWVuIGZ1bGxzY3JlZW4gYW5kIG5vcm1hbC5cclxuICAgICAqL1xyXG4gICAgVG9nZ2xlRnVsbHNjcmVlbigpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFRvZ2dsZUZ1bGxzY3JlZW5NZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVG9nZ2xlcyB0aGUgd2luZG93IGJldHdlZW4gbWF4aW1pc2VkIGFuZCBub3JtYWwuXHJcbiAgICAgKi9cclxuICAgIFRvZ2dsZU1heGltaXNlKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oVG9nZ2xlTWF4aW1pc2VNZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVG9nZ2xlcyB0aGUgd2luZG93IGJldHdlZW4gZnJhbWVsZXNzIGFuZCBub3JtYWwuXHJcbiAgICAgKi9cclxuICAgIFRvZ2dsZUZyYW1lbGVzcygpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpc1tjYWxsZXJTeW1dKFRvZ2dsZUZyYW1lbGVzc01ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBVbi1mdWxsc2NyZWVucyB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBVbkZ1bGxzY3JlZW4oKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShVbkZ1bGxzY3JlZW5NZXRob2QpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVW4tbWF4aW1pc2VzIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFVuTWF4aW1pc2UoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShVbk1heGltaXNlTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFVuLW1pbmltaXNlcyB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBVbk1pbmltaXNlKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oVW5NaW5pbWlzZU1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXR1cm5zIHRoZSB3aWR0aCBvZiB0aGUgd2luZG93LlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFRoZSBjdXJyZW50IHdpZHRoIG9mIHRoZSB3aW5kb3cuXHJcbiAgICAgKi9cclxuICAgIFdpZHRoKCk6IFByb21pc2U8bnVtYmVyPiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXNbY2FsbGVyU3ltXShXaWR0aE1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBab29tcyB0aGUgd2luZG93LlxyXG4gICAgICovXHJcbiAgICBab29tKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oWm9vbU1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbmNyZWFzZXMgdGhlIHpvb20gbGV2ZWwgb2YgdGhlIHdlYnZpZXcgY29udGVudC5cclxuICAgICAqL1xyXG4gICAgWm9vbUluKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oWm9vbUluTWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIERlY3JlYXNlcyB0aGUgem9vbSBsZXZlbCBvZiB0aGUgd2VidmlldyBjb250ZW50LlxyXG4gICAgICovXHJcbiAgICBab29tT3V0KCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oWm9vbU91dE1ldGhvZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXNldHMgdGhlIHpvb20gbGV2ZWwgb2YgdGhlIHdlYnZpZXcgY29udGVudC5cclxuICAgICAqL1xyXG4gICAgWm9vbVJlc2V0KCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzW2NhbGxlclN5bV0oWm9vbVJlc2V0TWV0aG9kKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEhhbmRsZXMgZmlsZSBkcm9wcyBvcmlnaW5hdGluZyBmcm9tIHBsYXRmb3JtLXNwZWNpZmljIGNvZGUgKGUuZy4sIG1hY09TIG5hdGl2ZSBkcmFnLWFuZC1kcm9wKS5cclxuICAgICAqIEdhdGhlcnMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRyb3AgdGFyZ2V0IGVsZW1lbnQgYW5kIHNlbmRzIGl0IGJhY2sgdG8gdGhlIEdvIGJhY2tlbmQuXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIGZpbGVuYW1lcyAtIEFuIGFycmF5IG9mIGZpbGUgcGF0aHMgKHN0cmluZ3MpIHRoYXQgd2VyZSBkcm9wcGVkLlxyXG4gICAgICogQHBhcmFtIHggLSBUaGUgeC1jb29yZGluYXRlIG9mIHRoZSBkcm9wIGV2ZW50LlxyXG4gICAgICogQHBhcmFtIHkgLSBUaGUgeS1jb29yZGluYXRlIG9mIHRoZSBkcm9wIGV2ZW50LlxyXG4gICAgICovXHJcbiAgICBIYW5kbGVQbGF0Zm9ybUZpbGVEcm9wKGZpbGVuYW1lczogc3RyaW5nW10sIHg6IG51bWJlciwgeTogbnVtYmVyKTogdm9pZCB7XHJcbiAgICAgICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQoeCwgeSk7XHJcblxyXG4gICAgICAgIC8vIE5FVzogQ2hlY2sgaWYgdGhlIGRyb3AgdGFyZ2V0IGlzIGEgdmFsaWQgZHJvcHpvbmVcclxuICAgICAgICBjb25zdCBkcm9wem9uZVRhcmdldCA9IGdldERyb3B6b25lRWxlbWVudChlbGVtZW50KTtcclxuXHJcbiAgICAgICAgaWYgKCFkcm9wem9uZVRhcmdldCkge1xyXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgV2FpbHMgUnVudGltZTogRHJvcCBvbiBlbGVtZW50IChvciBubyBlbGVtZW50KSBhdCAke3h9LCR7eX0gd2hpY2ggaXMgbm90IGEgZGVzaWduYXRlZCBkcm9wem9uZS4gSWdub3JpbmcuIEVsZW1lbnQ6YCwgZWxlbWVudCk7XHJcbiAgICAgICAgICAgIC8vIE5vIG5lZWQgdG8gY2FsbCBiYWNrZW5kIGlmIG5vdCBhIHZhbGlkIGRyb3B6b25lIHRhcmdldFxyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25zb2xlLmxvZyhgV2FpbHMgUnVudGltZTogRHJvcCBvbiBkZXNpZ25hdGVkIGRyb3B6b25lLiBFbGVtZW50IGF0ICgke3h9LCAke3l9KTpgLCBlbGVtZW50LCAnRWZmZWN0aXZlIGRyb3B6b25lOicsIGRyb3B6b25lVGFyZ2V0KTtcclxuICAgICAgICBjb25zdCBlbGVtZW50RGV0YWlscyA9IHtcclxuICAgICAgICAgICAgaWQ6IGRyb3B6b25lVGFyZ2V0LmlkLFxyXG4gICAgICAgICAgICBjbGFzc0xpc3Q6IEFycmF5LmZyb20oZHJvcHpvbmVUYXJnZXQuY2xhc3NMaXN0KSxcclxuICAgICAgICAgICAgYXR0cmlidXRlczoge30gYXMgeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfSxcclxuICAgICAgICB9O1xyXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZHJvcHpvbmVUYXJnZXQuYXR0cmlidXRlcy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICBjb25zdCBhdHRyID0gZHJvcHpvbmVUYXJnZXQuYXR0cmlidXRlc1tpXTtcclxuICAgICAgICAgICAgZWxlbWVudERldGFpbHMuYXR0cmlidXRlc1thdHRyLm5hbWVdID0gYXR0ci52YWx1ZTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XHJcbiAgICAgICAgICAgIGZpbGVuYW1lcyxcclxuICAgICAgICAgICAgeCxcclxuICAgICAgICAgICAgeSxcclxuICAgICAgICAgICAgZWxlbWVudERldGFpbHMsXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgdGhpc1tjYWxsZXJTeW1dKFdpbmRvd0Ryb3Bab25lRHJvcHBlZCwgcGF5bG9hZCk7XHJcbiAgICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBUaGUgd2luZG93IHdpdGhpbiB3aGljaCB0aGUgc2NyaXB0IGlzIHJ1bm5pbmcuXHJcbiAqL1xyXG5jb25zdCB0aGlzV2luZG93ID0gbmV3IFdpbmRvdygnJyk7XHJcblxyXG4vLyBORVc6IEdsb2JhbCBEcmFnIEV2ZW50IExpc3RlbmVyc1xyXG5mdW5jdGlvbiBzZXR1cEdsb2JhbERyb3B6b25lTGlzdGVuZXJzKCkge1xyXG4gICAgY29uc3QgZG9jRWxlbWVudCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcclxuICAgIGxldCBkcmFnRW50ZXJDb3VudGVyID0gMDsgLy8gVG8gaGFuZGxlIGRyYWdlbnRlci9kcmFnbGVhdmUgb24gY2hpbGQgZWxlbWVudHNcclxuXHJcbiAgICBkb2NFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdlbnRlcicsIChldmVudCkgPT4ge1xyXG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgaWYgKGV2ZW50LmRhdGFUcmFuc2ZlciAmJiBldmVudC5kYXRhVHJhbnNmZXIudHlwZXMuaW5jbHVkZXMoJ0ZpbGVzJykpIHtcclxuICAgICAgICAgICAgZHJhZ0VudGVyQ291bnRlcisrO1xyXG4gICAgICAgICAgICBjb25zdCB0YXJnZXRFbGVtZW50ID0gZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludChldmVudC5jbGllbnRYLCBldmVudC5jbGllbnRZKTtcclxuICAgICAgICAgICAgY29uc3QgZHJvcHpvbmUgPSBnZXREcm9wem9uZUVsZW1lbnQodGFyZ2V0RWxlbWVudCk7XHJcblxyXG4gICAgICAgICAgICAvLyBDbGVhciBwcmV2aW91cyBob3ZlciByZWdhcmRsZXNzLCB0aGVuIGFwcGx5IG5ldyBpZiB2YWxpZFxyXG4gICAgICAgICAgICBpZiAoY3VycmVudEhvdmVyZWREcm9wem9uZSAmJiBjdXJyZW50SG92ZXJlZERyb3B6b25lICE9PSBkcm9wem9uZSkge1xyXG4gICAgICAgICAgICAgICAgY3VycmVudEhvdmVyZWREcm9wem9uZS5jbGFzc0xpc3QucmVtb3ZlKERST1BaT05FX0hPVkVSX0NMQVNTKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaWYgKGRyb3B6b25lKSB7XHJcbiAgICAgICAgICAgICAgICBkcm9wem9uZS5jbGFzc0xpc3QuYWRkKERST1BaT05FX0hPVkVSX0NMQVNTKTtcclxuICAgICAgICAgICAgICAgIGV2ZW50LmRhdGFUcmFuc2Zlci5kcm9wRWZmZWN0ID0gJ2NvcHknO1xyXG4gICAgICAgICAgICAgICAgY3VycmVudEhvdmVyZWREcm9wem9uZSA9IGRyb3B6b25lO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgZXZlbnQuZGF0YVRyYW5zZmVyLmRyb3BFZmZlY3QgPSAnbm9uZSc7XHJcbiAgICAgICAgICAgICAgICBjdXJyZW50SG92ZXJlZERyb3B6b25lID0gbnVsbDsgLy8gRW5zdXJlIGl0J3MgY2xlYXJlZCBpZiBubyBkcm9wem9uZSBmb3VuZFxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfSwgZmFsc2UpO1xyXG5cclxuICAgIGRvY0VsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignZHJhZ292ZXInLCAoZXZlbnQpID0+IHtcclxuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyAvLyBOZWNlc3NhcnkgdG8gYWxsb3cgZHJvcFxyXG4gICAgICAgIGlmIChldmVudC5kYXRhVHJhbnNmZXIgJiYgZXZlbnQuZGF0YVRyYW5zZmVyLnR5cGVzLmluY2x1ZGVzKCdGaWxlcycpKSB7XHJcbiAgICAgICAgICAgIC8vIE5vIG5lZWQgdG8gcXVlcnkgZWxlbWVudEZyb21Qb2ludCBhZ2FpbiBpZiBhbHJlYWR5IGhhbmRsZWQgYnkgZHJhZ2VudGVyIGNvcnJlY3RseVxyXG4gICAgICAgICAgICAvLyBKdXN0IGVuc3VyZSBkcm9wRWZmZWN0IGlzIGNvbnRpbnVvdXNseSBzZXQgYmFzZWQgb24gY3VycmVudEhvdmVyZWREcm9wem9uZVxyXG4gICAgICAgICAgICBpZiAoY3VycmVudEhvdmVyZWREcm9wem9uZSkge1xyXG4gICAgICAgICAgICAgICAgIC8vIFJlLWFwcGx5IGNsYXNzIGp1c3QgaW4gY2FzZSBpdCB3YXMgcmVtb3ZlZCBieSBzb21lIG90aGVyIEpTXHJcbiAgICAgICAgICAgICAgICBpZighY3VycmVudEhvdmVyZWREcm9wem9uZS5jbGFzc0xpc3QuY29udGFpbnMoRFJPUFpPTkVfSE9WRVJfQ0xBU1MpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudEhvdmVyZWREcm9wem9uZS5jbGFzc0xpc3QuYWRkKERST1BaT05FX0hPVkVSX0NMQVNTKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGV2ZW50LmRhdGFUcmFuc2Zlci5kcm9wRWZmZWN0ID0gJ2NvcHknO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgZXZlbnQuZGF0YVRyYW5zZmVyLmRyb3BFZmZlY3QgPSAnbm9uZSc7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9LCBmYWxzZSk7XHJcblxyXG4gICAgZG9jRWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdkcmFnbGVhdmUnLCAoZXZlbnQpID0+IHtcclxuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgIGlmIChldmVudC5kYXRhVHJhbnNmZXIgJiYgZXZlbnQuZGF0YVRyYW5zZmVyLnR5cGVzLmluY2x1ZGVzKCdGaWxlcycpKSB7XHJcbiAgICAgICAgICAgIGRyYWdFbnRlckNvdW50ZXItLTtcclxuICAgICAgICAgICAgLy8gT25seSByZW1vdmUgaG92ZXIgaWYgZHJhZyB0cnVseSBsZWZ0IHRoZSB3aW5kb3cgb3IgdGhlIGxhc3QgZHJvcHpvbmVcclxuICAgICAgICAgICAgaWYgKGRyYWdFbnRlckNvdW50ZXIgPT09IDAgfHwgZXZlbnQucmVsYXRlZFRhcmdldCA9PT0gbnVsbCB8fCAoY3VycmVudEhvdmVyZWREcm9wem9uZSAmJiAhY3VycmVudEhvdmVyZWREcm9wem9uZS5jb250YWlucyhldmVudC5yZWxhdGVkVGFyZ2V0IGFzIE5vZGUpKSkge1xyXG4gICAgICAgICAgICAgICAgaWYgKGN1cnJlbnRIb3ZlcmVkRHJvcHpvbmUpIHtcclxuICAgICAgICAgICAgICAgICAgICBjdXJyZW50SG92ZXJlZERyb3B6b25lLmNsYXNzTGlzdC5yZW1vdmUoRFJPUFpPTkVfSE9WRVJfQ0xBU1MpO1xyXG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnRIb3ZlcmVkRHJvcHpvbmUgPSBudWxsO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZHJhZ0VudGVyQ291bnRlciA9IDA7IC8vIFJlc2V0IGNvdW50ZXIgaWYgaXQgd2VudCBuZWdhdGl2ZSBvciBsZWZ0IHdpbmRvd1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfSwgZmFsc2UpO1xyXG5cclxuICAgIGRvY0VsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignZHJvcCcsIChldmVudCkgPT4ge1xyXG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IC8vIFByZXZlbnQgZGVmYXVsdCBicm93c2VyIGZpbGUgaGFuZGxpbmdcclxuICAgICAgICBkcmFnRW50ZXJDb3VudGVyID0gMDsgLy8gUmVzZXQgY291bnRlclxyXG4gICAgICAgIGlmIChjdXJyZW50SG92ZXJlZERyb3B6b25lKSB7XHJcbiAgICAgICAgICAgIGN1cnJlbnRIb3ZlcmVkRHJvcHpvbmUuY2xhc3NMaXN0LnJlbW92ZShEUk9QWk9ORV9IT1ZFUl9DTEFTUyk7XHJcbiAgICAgICAgICAgIGN1cnJlbnRIb3ZlcmVkRHJvcHpvbmUgPSBudWxsO1xyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBUaGUgYWN0dWFsIGRyb3AgcHJvY2Vzc2luZyBpcyBpbml0aWF0ZWQgYnkgdGhlIG5hdGl2ZSBzaWRlIGNhbGxpbmcgSGFuZGxlUGxhdGZvcm1GaWxlRHJvcFxyXG4gICAgICAgIC8vIEhhbmRsZVBsYXRmb3JtRmlsZURyb3Agd2lsbCB0aGVuIGNoZWNrIGlmIHRoZSBkcm9wIHdhcyBvbiBhIHZhbGlkIHpvbmUuXHJcbiAgICB9LCBmYWxzZSk7XHJcbn1cclxuXHJcbi8vIEluaXRpYWxpemUgbGlzdGVuZXJzIHdoZW4gdGhlIHNjcmlwdCBsb2Fkc1xyXG5pZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB0eXBlb2YgZG9jdW1lbnQgIT09IFwidW5kZWZpbmVkXCIpIHtcclxuICAgIHNldHVwR2xvYmFsRHJvcHpvbmVMaXN0ZW5lcnMoKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgdGhpc1dpbmRvdztcclxuIiwgIi8qXHJcbiBfXHQgICBfX1x0ICBfIF9fXHJcbnwgfFx0IC8gL19fXyBfKF8pIC9fX19fXHJcbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cclxufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXHJcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xyXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXHJcbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcclxuKi9cclxuXHJcbmltcG9ydCAqIGFzIFJ1bnRpbWUgZnJvbSBcIi4uL0B3YWlsc2lvL3J1bnRpbWUvc3JjXCI7XHJcblxyXG4vLyBOT1RFOiB0aGUgZm9sbG93aW5nIG1ldGhvZHMgTVVTVCBiZSBpbXBvcnRlZCBleHBsaWNpdGx5IGJlY2F1c2Ugb2YgaG93IGVzYnVpbGQgaW5qZWN0aW9uIHdvcmtzXHJcbmltcG9ydCB7IEVuYWJsZSBhcyBFbmFibGVXTUwgfSBmcm9tIFwiLi4vQHdhaWxzaW8vcnVudGltZS9zcmMvd21sXCI7XHJcbmltcG9ydCB7IGRlYnVnTG9nIH0gZnJvbSBcIi4uL0B3YWlsc2lvL3J1bnRpbWUvc3JjL3V0aWxzXCI7XHJcblxyXG53aW5kb3cud2FpbHMgPSBSdW50aW1lO1xyXG5FbmFibGVXTUwoKTtcclxuXHJcbmlmIChERUJVRykge1xyXG4gICAgZGVidWdMb2coXCJXYWlscyBSdW50aW1lIExvYWRlZFwiKVxyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBuZXdSdW50aW1lQ2FsbGVyLCBvYmplY3ROYW1lcyB9IGZyb20gXCIuL3J1bnRpbWUuanNcIjtcclxuXHJcbmNvbnN0IGNhbGwgPSBuZXdSdW50aW1lQ2FsbGVyKG9iamVjdE5hbWVzLlN5c3RlbSk7XHJcblxyXG5jb25zdCBTeXN0ZW1Jc0RhcmtNb2RlID0gMDtcclxuY29uc3QgU3lzdGVtRW52aXJvbm1lbnQgPSAxO1xyXG5jb25zdCBBcHBsaWNhdGlvbkZpbGVzRHJvcHBlZFdpdGhDb250ZXh0ID0gMTAwOyAvLyBOZXcgbWV0aG9kIElEIGZvciBlbnJpY2hlZCBkcm9wIGV2ZW50XHJcblxyXG5jb25zdCBfaW52b2tlID0gKGZ1bmN0aW9uICgpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgICAgaWYgKCh3aW5kb3cgYXMgYW55KS5jaHJvbWU/LndlYnZpZXc/LnBvc3RNZXNzYWdlKSB7XHJcbiAgICAgICAgICAgIHJldHVybiAod2luZG93IGFzIGFueSkuY2hyb21lLndlYnZpZXcucG9zdE1lc3NhZ2UuYmluZCgod2luZG93IGFzIGFueSkuY2hyb21lLndlYnZpZXcpO1xyXG4gICAgICAgIH0gZWxzZSBpZiAoKHdpbmRvdyBhcyBhbnkpLndlYmtpdD8ubWVzc2FnZUhhbmRsZXJzPy5bJ2V4dGVybmFsJ10/LnBvc3RNZXNzYWdlKSB7XHJcbiAgICAgICAgICAgIHJldHVybiAod2luZG93IGFzIGFueSkud2Via2l0Lm1lc3NhZ2VIYW5kbGVyc1snZXh0ZXJuYWwnXS5wb3N0TWVzc2FnZS5iaW5kKCh3aW5kb3cgYXMgYW55KS53ZWJraXQubWVzc2FnZUhhbmRsZXJzWydleHRlcm5hbCddKTtcclxuICAgICAgICB9XHJcbiAgICB9IGNhdGNoKGUpIHt9XHJcblxyXG4gICAgY29uc29sZS53YXJuKCdcXG4lY1x1MjZBMFx1RkUwRiBCcm93c2VyIEVudmlyb25tZW50IERldGVjdGVkICVjXFxuXFxuJWNPbmx5IFVJIHByZXZpZXdzIGFyZSBhdmFpbGFibGUgaW4gdGhlIGJyb3dzZXIuIEZvciBmdWxsIGZ1bmN0aW9uYWxpdHksIHBsZWFzZSBydW4gdGhlIGFwcGxpY2F0aW9uIGluIGRlc2t0b3AgbW9kZS5cXG5Nb3JlIGluZm9ybWF0aW9uIGF0OiBodHRwczovL3YzLndhaWxzLmlvL2xlYXJuL2J1aWxkLyN1c2luZy1hLWJyb3dzZXItZm9yLWRldmVsb3BtZW50XFxuJyxcclxuICAgICAgICAnYmFja2dyb3VuZDogI2ZmZmZmZjsgY29sb3I6ICMwMDAwMDA7IGZvbnQtd2VpZ2h0OiBib2xkOyBwYWRkaW5nOiA0cHggOHB4OyBib3JkZXItcmFkaXVzOiA0cHg7IGJvcmRlcjogMnB4IHNvbGlkICMwMDAwMDA7JyxcclxuICAgICAgICAnYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7JyxcclxuICAgICAgICAnY29sb3I6ICNmZmZmZmY7IGZvbnQtc3R5bGU6IGl0YWxpYzsgZm9udC13ZWlnaHQ6IGJvbGQ7Jyk7XHJcbiAgICByZXR1cm4gbnVsbDtcclxufSkoKTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBpbnZva2UobXNnOiBhbnkpOiB2b2lkIHtcclxuICAgIF9pbnZva2U/Lihtc2cpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBzeXN0ZW0gZGFyayBtb2RlIHN0YXR1cy5cclxuICpcclxuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSBib29sZWFuIHZhbHVlIGluZGljYXRpbmcgaWYgdGhlIHN5c3RlbSBpcyBpbiBkYXJrIG1vZGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gSXNEYXJrTW9kZSgpOiBQcm9taXNlPGJvb2xlYW4+IHtcclxuICAgIHJldHVybiBjYWxsKFN5c3RlbUlzRGFya01vZGUpO1xyXG59XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyB0aGUgY2FwYWJpbGl0aWVzIG9mIHRoZSBhcHBsaWNhdGlvbiBmcm9tIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nIHRoZSBjYXBhYmlsaXRpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gQ2FwYWJpbGl0aWVzKCk6IFByb21pc2U8UmVjb3JkPHN0cmluZywgYW55Pj4ge1xyXG4gICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goXCIvd2FpbHMvY2FwYWJpbGl0aWVzXCIpO1xyXG4gICAgaWYgKHJlc3BvbnNlLm9rKSB7XHJcbiAgICAgICAgcmV0dXJuIHJlc3BvbnNlLmpzb24oKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiY291bGQgbm90IGZldGNoIGNhcGFiaWxpdGllczogXCIgKyByZXNwb25zZS5zdGF0dXNUZXh0KTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBPU0luZm8ge1xyXG4gICAgLyoqIFRoZSBicmFuZGluZyBvZiB0aGUgT1MuICovXHJcbiAgICBCcmFuZGluZzogc3RyaW5nO1xyXG4gICAgLyoqIFRoZSBJRCBvZiB0aGUgT1MuICovXHJcbiAgICBJRDogc3RyaW5nO1xyXG4gICAgLyoqIFRoZSBuYW1lIG9mIHRoZSBPUy4gKi9cclxuICAgIE5hbWU6IHN0cmluZztcclxuICAgIC8qKiBUaGUgdmVyc2lvbiBvZiB0aGUgT1MuICovXHJcbiAgICBWZXJzaW9uOiBzdHJpbmc7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgRW52aXJvbm1lbnRJbmZvIHtcclxuICAgIC8qKiBUaGUgYXJjaGl0ZWN0dXJlIG9mIHRoZSBzeXN0ZW0uICovXHJcbiAgICBBcmNoOiBzdHJpbmc7XHJcbiAgICAvKiogVHJ1ZSBpZiB0aGUgYXBwbGljYXRpb24gaXMgcnVubmluZyBpbiBkZWJ1ZyBtb2RlLCBvdGhlcndpc2UgZmFsc2UuICovXHJcbiAgICBEZWJ1ZzogYm9vbGVhbjtcclxuICAgIC8qKiBUaGUgb3BlcmF0aW5nIHN5c3RlbSBpbiB1c2UuICovXHJcbiAgICBPUzogc3RyaW5nO1xyXG4gICAgLyoqIERldGFpbHMgb2YgdGhlIG9wZXJhdGluZyBzeXN0ZW0uICovXHJcbiAgICBPU0luZm86IE9TSW5mbztcclxuICAgIC8qKiBBZGRpdGlvbmFsIHBsYXRmb3JtIGluZm9ybWF0aW9uLiAqL1xyXG4gICAgUGxhdGZvcm1JbmZvOiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGVudmlyb25tZW50IGRldGFpbHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nIE9TIGFuZCBzeXN0ZW0gYXJjaGl0ZWN0dXJlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIEVudmlyb25tZW50KCk6IFByb21pc2U8RW52aXJvbm1lbnRJbmZvPiB7XHJcbiAgICByZXR1cm4gY2FsbChTeXN0ZW1FbnZpcm9ubWVudCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGN1cnJlbnQgb3BlcmF0aW5nIHN5c3RlbSBpcyBXaW5kb3dzLlxyXG4gKlxyXG4gKiBAcmV0dXJuIFRydWUgaWYgdGhlIG9wZXJhdGluZyBzeXN0ZW0gaXMgV2luZG93cywgb3RoZXJ3aXNlIGZhbHNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIElzV2luZG93cygpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB3aW5kb3cuX3dhaWxzLmVudmlyb25tZW50Lk9TID09PSBcIndpbmRvd3NcIjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgY3VycmVudCBvcGVyYXRpbmcgc3lzdGVtIGlzIExpbnV4LlxyXG4gKlxyXG4gKiBAcmV0dXJucyBSZXR1cm5zIHRydWUgaWYgdGhlIGN1cnJlbnQgb3BlcmF0aW5nIHN5c3RlbSBpcyBMaW51eCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIElzTGludXgoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gd2luZG93Ll93YWlscy5lbnZpcm9ubWVudC5PUyA9PT0gXCJsaW51eFwiO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBjdXJyZW50IGVudmlyb25tZW50IGlzIGEgbWFjT1Mgb3BlcmF0aW5nIHN5c3RlbS5cclxuICpcclxuICogQHJldHVybnMgVHJ1ZSBpZiB0aGUgZW52aXJvbm1lbnQgaXMgbWFjT1MsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBJc01hYygpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB3aW5kb3cuX3dhaWxzLmVudmlyb25tZW50Lk9TID09PSBcImRhcndpblwiO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBjdXJyZW50IGVudmlyb25tZW50IGFyY2hpdGVjdHVyZSBpcyBBTUQ2NC5cclxuICpcclxuICogQHJldHVybnMgVHJ1ZSBpZiB0aGUgY3VycmVudCBlbnZpcm9ubWVudCBhcmNoaXRlY3R1cmUgaXMgQU1ENjQsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBJc0FNRDY0KCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHdpbmRvdy5fd2FpbHMuZW52aXJvbm1lbnQuQXJjaCA9PT0gXCJhbWQ2NFwiO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBjdXJyZW50IGFyY2hpdGVjdHVyZSBpcyBBUk0uXHJcbiAqXHJcbiAqIEByZXR1cm5zIFRydWUgaWYgdGhlIGN1cnJlbnQgYXJjaGl0ZWN0dXJlIGlzIEFSTSwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIElzQVJNKCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHdpbmRvdy5fd2FpbHMuZW52aXJvbm1lbnQuQXJjaCA9PT0gXCJhcm1cIjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgY3VycmVudCBlbnZpcm9ubWVudCBpcyBBUk02NCBhcmNoaXRlY3R1cmUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIFJldHVybnMgdHJ1ZSBpZiB0aGUgZW52aXJvbm1lbnQgaXMgQVJNNjQgYXJjaGl0ZWN0dXJlLCBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBJc0FSTTY0KCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHdpbmRvdy5fd2FpbHMuZW52aXJvbm1lbnQuQXJjaCA9PT0gXCJhcm02NFwiO1xyXG59XHJcblxyXG4vKipcclxuICogUmVwb3J0cyB3aGV0aGVyIHRoZSBhcHAgaXMgYmVpbmcgcnVuIGluIGRlYnVnIG1vZGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIFRydWUgaWYgdGhlIGFwcCBpcyBiZWluZyBydW4gaW4gZGVidWcgbW9kZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBJc0RlYnVnKCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIEJvb2xlYW4od2luZG93Ll93YWlscy5lbnZpcm9ubWVudC5EZWJ1Zyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGZpbGUgZHJvcHMgb3JpZ2luYXRpbmcgZnJvbSBwbGF0Zm9ybS1zcGVjaWZpYyBjb2RlIChlLmcuLCBtYWNPUyBuYXRpdmUgZHJhZy1hbmQtZHJvcCkuXHJcbiAqIEdhdGhlcnMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRyb3AgdGFyZ2V0IGVsZW1lbnQgYW5kIHNlbmRzIGl0IGJhY2sgdG8gdGhlIEdvIGJhY2tlbmQuXHJcbiAqXHJcbiAqIEBwYXJhbSBmaWxlbmFtZXMgLSBBbiBhcnJheSBvZiBmaWxlIHBhdGhzIChzdHJpbmdzKSB0aGF0IHdlcmUgZHJvcHBlZC5cclxuICogQHBhcmFtIHggLSBUaGUgeC1jb29yZGluYXRlIG9mIHRoZSBkcm9wIGV2ZW50LlxyXG4gKiBAcGFyYW0geSAtIFRoZSB5LWNvb3JkaW5hdGUgb2YgdGhlIGRyb3AgZXZlbnQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gSGFuZGxlUGxhdGZvcm1GaWxlRHJvcChmaWxlbmFtZXM6IHN0cmluZ1tdLCB4OiBudW1iZXIsIHk6IG51bWJlcik6IHZvaWQge1xyXG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQoeCwgeSk7XHJcbiAgICBjb25zdCBlbGVtZW50SWQgPSBlbGVtZW50ID8gZWxlbWVudC5pZCA6ICcnO1xyXG4gICAgY29uc3QgY2xhc3NMaXN0ID0gZWxlbWVudCA/IEFycmF5LmZyb20oZWxlbWVudC5jbGFzc0xpc3QpIDogW107XHJcblxyXG4gICAgY29uc3QgcGF5bG9hZCA9IHtcclxuICAgICAgICBmaWxlbmFtZXMsXHJcbiAgICAgICAgeCxcclxuICAgICAgICB5LFxyXG4gICAgICAgIGVsZW1lbnRJZCxcclxuICAgICAgICBjbGFzc0xpc3QsXHJcbiAgICB9O1xyXG5cclxuICAgIGNhbGwoQXBwbGljYXRpb25GaWxlc0Ryb3BwZWRXaXRoQ29udGV4dCwgcGF5bG9hZClcclxuICAgICAgICAudGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgIC8vIE9wdGlvbmFsOiBMb2cgc3VjY2VzcyBvciBoYW5kbGUgaWYgbmVlZGVkXHJcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiUGxhdGZvcm0gZmlsZSBkcm9wIHByb2Nlc3NlZCBhbmQgc2VudCB0byBHby5cIik7XHJcbiAgICAgICAgfSlcclxuICAgICAgICAuY2F0Y2goZXJyID0+IHtcclxuICAgICAgICAgICAgLy8gT3B0aW9uYWw6IExvZyBlcnJvclxyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3Igc2VuZGluZyBwbGF0Zm9ybSBmaWxlIGRyb3AgdG8gR286XCIsIGVycik7XHJcbiAgICAgICAgfSk7XHJcbn1cclxuXHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBuZXdSdW50aW1lQ2FsbGVyLCBvYmplY3ROYW1lcyB9IGZyb20gXCIuL3J1bnRpbWUuanNcIjtcclxuaW1wb3J0IHsgSXNEZWJ1ZyB9IGZyb20gXCIuL3N5c3RlbS5qc1wiO1xyXG5pbXBvcnQgeyBldmVudFRhcmdldCB9IGZyb20gXCIuL3V0aWxzXCI7XHJcblxyXG4vLyBzZXR1cFxyXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignY29udGV4dG1lbnUnLCBjb250ZXh0TWVudUhhbmRsZXIpO1xyXG5cclxuY29uc3QgY2FsbCA9IG5ld1J1bnRpbWVDYWxsZXIob2JqZWN0TmFtZXMuQ29udGV4dE1lbnUpO1xyXG5cclxuY29uc3QgQ29udGV4dE1lbnVPcGVuID0gMDtcclxuXHJcbmZ1bmN0aW9uIG9wZW5Db250ZXh0TWVudShpZDogc3RyaW5nLCB4OiBudW1iZXIsIHk6IG51bWJlciwgZGF0YTogYW55KTogdm9pZCB7XHJcbiAgICB2b2lkIGNhbGwoQ29udGV4dE1lbnVPcGVuLCB7aWQsIHgsIHksIGRhdGF9KTtcclxufVxyXG5cclxuZnVuY3Rpb24gY29udGV4dE1lbnVIYW5kbGVyKGV2ZW50OiBNb3VzZUV2ZW50KSB7XHJcbiAgICBjb25zdCB0YXJnZXQgPSBldmVudFRhcmdldChldmVudCk7XHJcblxyXG4gICAgLy8gQ2hlY2sgZm9yIGN1c3RvbSBjb250ZXh0IG1lbnVcclxuICAgIGNvbnN0IGN1c3RvbUNvbnRleHRNZW51ID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUodGFyZ2V0KS5nZXRQcm9wZXJ0eVZhbHVlKFwiLS1jdXN0b20tY29udGV4dG1lbnVcIikudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db250ZXh0TWVudSkge1xyXG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgY29uc3QgZGF0YSA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKHRhcmdldCkuZ2V0UHJvcGVydHlWYWx1ZShcIi0tY3VzdG9tLWNvbnRleHRtZW51LWRhdGFcIik7XHJcbiAgICAgICAgb3BlbkNvbnRleHRNZW51KGN1c3RvbUNvbnRleHRNZW51LCBldmVudC5jbGllbnRYLCBldmVudC5jbGllbnRZLCBkYXRhKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgcHJvY2Vzc0RlZmF1bHRDb250ZXh0TWVudShldmVudCwgdGFyZ2V0KTtcclxuICAgIH1cclxufVxyXG5cclxuXHJcbi8qXHJcbi0tZGVmYXVsdC1jb250ZXh0bWVudTogYXV0bzsgKGRlZmF1bHQpIHdpbGwgc2hvdyB0aGUgZGVmYXVsdCBjb250ZXh0IG1lbnUgaWYgY29udGVudEVkaXRhYmxlIGlzIHRydWUgT1IgdGV4dCBoYXMgYmVlbiBzZWxlY3RlZCBPUiBlbGVtZW50IGlzIGlucHV0IG9yIHRleHRhcmVhXHJcbi0tZGVmYXVsdC1jb250ZXh0bWVudTogc2hvdzsgd2lsbCBhbHdheXMgc2hvdyB0aGUgZGVmYXVsdCBjb250ZXh0IG1lbnVcclxuLS1kZWZhdWx0LWNvbnRleHRtZW51OiBoaWRlOyB3aWxsIGFsd2F5cyBoaWRlIHRoZSBkZWZhdWx0IGNvbnRleHQgbWVudVxyXG5cclxuVGhpcyBydWxlIGlzIGluaGVyaXRlZCBsaWtlIG5vcm1hbCBDU1MgcnVsZXMsIHNvIG5lc3Rpbmcgd29ya3MgYXMgZXhwZWN0ZWRcclxuKi9cclxuZnVuY3Rpb24gcHJvY2Vzc0RlZmF1bHRDb250ZXh0TWVudShldmVudDogTW91c2VFdmVudCwgdGFyZ2V0OiBIVE1MRWxlbWVudCkge1xyXG4gICAgLy8gRGVidWcgYnVpbGRzIGFsd2F5cyBzaG93IHRoZSBtZW51XHJcbiAgICBpZiAoSXNEZWJ1ZygpKSB7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgZGVmYXVsdCBjb250ZXh0IG1lbnVcclxuICAgIHN3aXRjaCAod2luZG93LmdldENvbXB1dGVkU3R5bGUodGFyZ2V0KS5nZXRQcm9wZXJ0eVZhbHVlKFwiLS1kZWZhdWx0LWNvbnRleHRtZW51XCIpLnRyaW0oKSkge1xyXG4gICAgICAgIGNhc2UgJ3Nob3cnOlxyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgY2FzZSAnaGlkZSc6XHJcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiBjb250ZW50RWRpdGFibGUgaXMgdHJ1ZVxyXG4gICAgaWYgKHRhcmdldC5pc0NvbnRlbnRFZGl0YWJsZSkge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0ZXh0IGhhcyBiZWVuIHNlbGVjdGVkXHJcbiAgICBjb25zdCBzZWxlY3Rpb24gPSB3aW5kb3cuZ2V0U2VsZWN0aW9uKCk7XHJcbiAgICBjb25zdCBoYXNTZWxlY3Rpb24gPSBzZWxlY3Rpb24gJiYgc2VsZWN0aW9uLnRvU3RyaW5nKCkubGVuZ3RoID4gMDtcclxuICAgIGlmIChoYXNTZWxlY3Rpb24pIHtcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNlbGVjdGlvbi5yYW5nZUNvdW50OyBpKyspIHtcclxuICAgICAgICAgICAgY29uc3QgcmFuZ2UgPSBzZWxlY3Rpb24uZ2V0UmFuZ2VBdChpKTtcclxuICAgICAgICAgICAgY29uc3QgcmVjdHMgPSByYW5nZS5nZXRDbGllbnRSZWN0cygpO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHJlY3RzLmxlbmd0aDsgaisrKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCByZWN0ID0gcmVjdHNbal07XHJcbiAgICAgICAgICAgICAgICBpZiAoZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludChyZWN0LmxlZnQsIHJlY3QudG9wKSA9PT0gdGFyZ2V0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIGlmIHRhZyBpcyBpbnB1dCBvciB0ZXh0YXJlYS5cclxuICAgIGlmICh0YXJnZXQgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50IHx8IHRhcmdldCBpbnN0YW5jZW9mIEhUTUxUZXh0QXJlYUVsZW1lbnQpIHtcclxuICAgICAgICBpZiAoaGFzU2VsZWN0aW9uIHx8ICghdGFyZ2V0LnJlYWRPbmx5ICYmICF0YXJnZXQuZGlzYWJsZWQpKSB7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gaGlkZSBkZWZhdWx0IGNvbnRleHQgbWVudVxyXG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxufVxyXG4iLCAiLypcclxuIF9cdCAgIF9fXHQgIF8gX19cclxufCB8XHQgLyAvX19fIF8oXykgL19fX19cclxufCB8IC98IC8gLyBfXyBgLyAvIC8gX19fL1xyXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcclxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXHJcblRoZSBlbGVjdHJvbiBhbHRlcm5hdGl2ZSBmb3IgR29cclxuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxyXG4qL1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIHRoZSBzcGVjaWZpZWQga2V5IGZyb20gdGhlIGZsYWcgbWFwLlxyXG4gKlxyXG4gKiBAcGFyYW0ga2V5IC0gVGhlIGtleSB0byByZXRyaWV2ZSB0aGUgdmFsdWUgZm9yLlxyXG4gKiBAcmV0dXJuIFRoZSB2YWx1ZSBhc3NvY2lhdGVkIHdpdGggdGhlIHNwZWNpZmllZCBrZXkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gR2V0RmxhZyhrZXk6IHN0cmluZyk6IGFueSB7XHJcbiAgICB0cnkge1xyXG4gICAgICAgIHJldHVybiB3aW5kb3cuX3dhaWxzLmZsYWdzW2tleV07XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVW5hYmxlIHRvIHJldHJpZXZlIGZsYWcgJ1wiICsga2V5ICsgXCInOiBcIiArIGUsIHsgY2F1c2U6IGUgfSk7XHJcbiAgICB9XHJcbn1cclxuIiwgIi8qXHJcbiBfXHQgICBfX1x0ICBfIF9fXHJcbnwgfFx0IC8gL19fXyBfKF8pIC9fX19fXHJcbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cclxufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXHJcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xyXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXHJcbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcclxuKi9cclxuXHJcbmltcG9ydCB7IGludm9rZSwgSXNXaW5kb3dzIH0gZnJvbSBcIi4vc3lzdGVtLmpzXCI7XHJcbmltcG9ydCB7IEdldEZsYWcgfSBmcm9tIFwiLi9mbGFncy5qc1wiO1xyXG5pbXBvcnQgeyBjYW5UcmFja0J1dHRvbnMsIGV2ZW50VGFyZ2V0IH0gZnJvbSBcIi4vdXRpbHMuanNcIjtcclxuXHJcbi8vIFNldHVwXHJcbmxldCBjYW5EcmFnID0gZmFsc2U7XHJcbmxldCBkcmFnZ2luZyA9IGZhbHNlO1xyXG5cclxubGV0IHJlc2l6YWJsZSA9IGZhbHNlO1xyXG5sZXQgY2FuUmVzaXplID0gZmFsc2U7XHJcbmxldCByZXNpemluZyA9IGZhbHNlO1xyXG5sZXQgcmVzaXplRWRnZTogc3RyaW5nID0gXCJcIjtcclxubGV0IGRlZmF1bHRDdXJzb3IgPSBcImF1dG9cIjtcclxuXHJcbmxldCBidXR0b25zID0gMDtcclxuY29uc3QgYnV0dG9uc1RyYWNrZWQgPSBjYW5UcmFja0J1dHRvbnMoKTtcclxuXHJcbndpbmRvdy5fd2FpbHMgPSB3aW5kb3cuX3dhaWxzIHx8IHt9O1xyXG53aW5kb3cuX3dhaWxzLnNldFJlc2l6YWJsZSA9ICh2YWx1ZTogYm9vbGVhbik6IHZvaWQgPT4ge1xyXG4gICAgcmVzaXphYmxlID0gdmFsdWU7XHJcbiAgICBpZiAoIXJlc2l6YWJsZSkge1xyXG4gICAgICAgIC8vIFN0b3AgcmVzaXppbmcgaWYgaW4gcHJvZ3Jlc3MuXHJcbiAgICAgICAgY2FuUmVzaXplID0gcmVzaXppbmcgPSBmYWxzZTtcclxuICAgICAgICBzZXRSZXNpemUoKTtcclxuICAgIH1cclxufTtcclxuXHJcbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWRvd24nLCB1cGRhdGUsIHsgY2FwdHVyZTogdHJ1ZSB9KTtcclxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIHVwZGF0ZSwgeyBjYXB0dXJlOiB0cnVlIH0pO1xyXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIHVwZGF0ZSwgeyBjYXB0dXJlOiB0cnVlIH0pO1xyXG5mb3IgKGNvbnN0IGV2IG9mIFsnY2xpY2snLCAnY29udGV4dG1lbnUnLCAnZGJsY2xpY2snXSkge1xyXG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoZXYsIHN1cHByZXNzRXZlbnQsIHsgY2FwdHVyZTogdHJ1ZSB9KTtcclxufVxyXG5cclxuZnVuY3Rpb24gc3VwcHJlc3NFdmVudChldmVudDogRXZlbnQpIHtcclxuICAgIC8vIFN1cHByZXNzIGNsaWNrIGV2ZW50cyB3aGlsZSByZXNpemluZyBvciBkcmFnZ2luZy5cclxuICAgIGlmIChkcmFnZ2luZyB8fCByZXNpemluZykge1xyXG4gICAgICAgIGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpO1xyXG4gICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xyXG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICB9XHJcbn1cclxuXHJcbi8vIFVzZSBjb25zdGFudHMgdG8gYXZvaWQgY29tcGFyaW5nIHN0cmluZ3MgbXVsdGlwbGUgdGltZXMuXHJcbmNvbnN0IE1vdXNlRG93biA9IDA7XHJcbmNvbnN0IE1vdXNlVXAgICA9IDE7XHJcbmNvbnN0IE1vdXNlTW92ZSA9IDI7XHJcblxyXG5mdW5jdGlvbiB1cGRhdGUoZXZlbnQ6IE1vdXNlRXZlbnQpIHtcclxuICAgIC8vIFdpbmRvd3Mgc3VwcHJlc3NlcyBtb3VzZSBldmVudHMgYXQgdGhlIGVuZCBvZiBkcmFnZ2luZyBvciByZXNpemluZyxcclxuICAgIC8vIHNvIHdlIG5lZWQgdG8gYmUgc21hcnQgYW5kIHN5bnRoZXNpemUgYnV0dG9uIGV2ZW50cy5cclxuXHJcbiAgICBsZXQgZXZlbnRUeXBlOiBudW1iZXIsIGV2ZW50QnV0dG9ucyA9IGV2ZW50LmJ1dHRvbnM7XHJcbiAgICBzd2l0Y2ggKGV2ZW50LnR5cGUpIHtcclxuICAgICAgICBjYXNlICdtb3VzZWRvd24nOlxyXG4gICAgICAgICAgICBldmVudFR5cGUgPSBNb3VzZURvd247XHJcbiAgICAgICAgICAgIGlmICghYnV0dG9uc1RyYWNrZWQpIHsgZXZlbnRCdXR0b25zID0gYnV0dG9ucyB8ICgxIDw8IGV2ZW50LmJ1dHRvbik7IH1cclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnbW91c2V1cCc6XHJcbiAgICAgICAgICAgIGV2ZW50VHlwZSA9IE1vdXNlVXA7XHJcbiAgICAgICAgICAgIGlmICghYnV0dG9uc1RyYWNrZWQpIHsgZXZlbnRCdXR0b25zID0gYnV0dG9ucyAmIH4oMSA8PCBldmVudC5idXR0b24pOyB9XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgIGV2ZW50VHlwZSA9IE1vdXNlTW92ZTtcclxuICAgICAgICAgICAgaWYgKCFidXR0b25zVHJhY2tlZCkgeyBldmVudEJ1dHRvbnMgPSBidXR0b25zOyB9XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCByZWxlYXNlZCA9IGJ1dHRvbnMgJiB+ZXZlbnRCdXR0b25zO1xyXG4gICAgbGV0IHByZXNzZWQgPSBldmVudEJ1dHRvbnMgJiB+YnV0dG9ucztcclxuXHJcbiAgICBidXR0b25zID0gZXZlbnRCdXR0b25zO1xyXG5cclxuICAgIC8vIFN5bnRoZXNpemUgYSByZWxlYXNlLXByZXNzIHNlcXVlbmNlIGlmIHdlIGRldGVjdCBhIHByZXNzIG9mIGFuIGFscmVhZHkgcHJlc3NlZCBidXR0b24uXHJcbiAgICBpZiAoZXZlbnRUeXBlID09PSBNb3VzZURvd24gJiYgIShwcmVzc2VkICYgZXZlbnQuYnV0dG9uKSkge1xyXG4gICAgICAgIHJlbGVhc2VkIHw9ICgxIDw8IGV2ZW50LmJ1dHRvbik7XHJcbiAgICAgICAgcHJlc3NlZCB8PSAoMSA8PCBldmVudC5idXR0b24pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFN1cHByZXNzIGFsbCBidXR0b24gZXZlbnRzIGR1cmluZyBkcmFnZ2luZyBhbmQgcmVzaXppbmcsXHJcbiAgICAvLyB1bmxlc3MgdGhpcyBpcyBhIG1vdXNldXAgZXZlbnQgdGhhdCBpcyBlbmRpbmcgYSBkcmFnIGFjdGlvbi5cclxuICAgIGlmIChcclxuICAgICAgICBldmVudFR5cGUgIT09IE1vdXNlTW92ZSAvLyBGYXN0IHBhdGggZm9yIG1vdXNlbW92ZVxyXG4gICAgICAgICYmIHJlc2l6aW5nXHJcbiAgICAgICAgfHwgKFxyXG4gICAgICAgICAgICBkcmFnZ2luZ1xyXG4gICAgICAgICAgICAmJiAoXHJcbiAgICAgICAgICAgICAgICBldmVudFR5cGUgPT09IE1vdXNlRG93blxyXG4gICAgICAgICAgICAgICAgfHwgZXZlbnQuYnV0dG9uICE9PSAwXHJcbiAgICAgICAgICAgIClcclxuICAgICAgICApXHJcbiAgICApIHtcclxuICAgICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcclxuICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcclxuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEhhbmRsZSByZWxlYXNlc1xyXG4gICAgaWYgKHJlbGVhc2VkICYgMSkgeyBwcmltYXJ5VXAoZXZlbnQpOyB9XHJcbiAgICAvLyBIYW5kbGUgcHJlc3Nlc1xyXG4gICAgaWYgKHByZXNzZWQgJiAxKSB7IHByaW1hcnlEb3duKGV2ZW50KTsgfVxyXG5cclxuICAgIC8vIEhhbmRsZSBtb3VzZW1vdmVcclxuICAgIGlmIChldmVudFR5cGUgPT09IE1vdXNlTW92ZSkgeyBvbk1vdXNlTW92ZShldmVudCk7IH07XHJcbn1cclxuXHJcbmZ1bmN0aW9uIHByaW1hcnlEb3duKGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XHJcbiAgICAvLyBSZXNldCByZWFkaW5lc3Mgc3RhdGUuXHJcbiAgICBjYW5EcmFnID0gZmFsc2U7XHJcbiAgICBjYW5SZXNpemUgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBJZ25vcmUgcmVwZWF0ZWQgY2xpY2tzIG9uIG1hY09TIGFuZCBMaW51eC5cclxuICAgIGlmICghSXNXaW5kb3dzKCkpIHtcclxuICAgICAgICBpZiAoZXZlbnQudHlwZSA9PT0gJ21vdXNlZG93bicgJiYgZXZlbnQuYnV0dG9uID09PSAwICYmIGV2ZW50LmRldGFpbCAhPT0gMSkge1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGlmIChyZXNpemVFZGdlKSB7XHJcbiAgICAgICAgLy8gUmVhZHkgdG8gcmVzaXplIGlmIHRoZSBwcmltYXJ5IGJ1dHRvbiB3YXMgcHJlc3NlZCBmb3IgdGhlIGZpcnN0IHRpbWUuXHJcbiAgICAgICAgY2FuUmVzaXplID0gdHJ1ZTtcclxuICAgICAgICAvLyBEbyBub3Qgc3RhcnQgZHJhZyBvcGVyYXRpb25zIHdoZW4gb24gcmVzaXplIGVkZ2VzLlxyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXRyaWV2ZSB0YXJnZXQgZWxlbWVudFxyXG4gICAgY29uc3QgdGFyZ2V0ID0gZXZlbnRUYXJnZXQoZXZlbnQpO1xyXG5cclxuICAgIC8vIFJlYWR5IHRvIGRyYWcgaWYgdGhlIHByaW1hcnkgYnV0dG9uIHdhcyBwcmVzc2VkIGZvciB0aGUgZmlyc3QgdGltZSBvbiBhIGRyYWdnYWJsZSBlbGVtZW50LlxyXG4gICAgLy8gSWdub3JlIGNsaWNrcyBvbiB0aGUgc2Nyb2xsYmFyLlxyXG4gICAgY29uc3Qgc3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZSh0YXJnZXQpO1xyXG4gICAgY2FuRHJhZyA9IChcclxuICAgICAgICBzdHlsZS5nZXRQcm9wZXJ0eVZhbHVlKFwiLS13YWlscy1kcmFnZ2FibGVcIikudHJpbSgpID09PSBcImRyYWdcIlxyXG4gICAgICAgICYmIChcclxuICAgICAgICAgICAgZXZlbnQub2Zmc2V0WCAtIHBhcnNlRmxvYXQoc3R5bGUucGFkZGluZ0xlZnQpIDwgdGFyZ2V0LmNsaWVudFdpZHRoXHJcbiAgICAgICAgICAgICYmIGV2ZW50Lm9mZnNldFkgLSBwYXJzZUZsb2F0KHN0eWxlLnBhZGRpbmdUb3ApIDwgdGFyZ2V0LmNsaWVudEhlaWdodFxyXG4gICAgICAgIClcclxuICAgICk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIHByaW1hcnlVcChldmVudDogTW91c2VFdmVudCkge1xyXG4gICAgLy8gU3RvcCBkcmFnZ2luZyBhbmQgcmVzaXppbmcuXHJcbiAgICBjYW5EcmFnID0gZmFsc2U7XHJcbiAgICBkcmFnZ2luZyA9IGZhbHNlO1xyXG4gICAgY2FuUmVzaXplID0gZmFsc2U7XHJcbiAgICByZXNpemluZyA9IGZhbHNlO1xyXG59XHJcblxyXG5jb25zdCBjdXJzb3JGb3JFZGdlID0gT2JqZWN0LmZyZWV6ZSh7XHJcbiAgICBcInNlLXJlc2l6ZVwiOiBcIm53c2UtcmVzaXplXCIsXHJcbiAgICBcInN3LXJlc2l6ZVwiOiBcIm5lc3ctcmVzaXplXCIsXHJcbiAgICBcIm53LXJlc2l6ZVwiOiBcIm53c2UtcmVzaXplXCIsXHJcbiAgICBcIm5lLXJlc2l6ZVwiOiBcIm5lc3ctcmVzaXplXCIsXHJcbiAgICBcInctcmVzaXplXCI6IFwiZXctcmVzaXplXCIsXHJcbiAgICBcIm4tcmVzaXplXCI6IFwibnMtcmVzaXplXCIsXHJcbiAgICBcInMtcmVzaXplXCI6IFwibnMtcmVzaXplXCIsXHJcbiAgICBcImUtcmVzaXplXCI6IFwiZXctcmVzaXplXCIsXHJcbn0pXHJcblxyXG5mdW5jdGlvbiBzZXRSZXNpemUoZWRnZT86IGtleW9mIHR5cGVvZiBjdXJzb3JGb3JFZGdlKTogdm9pZCB7XHJcbiAgICBpZiAoZWRnZSkge1xyXG4gICAgICAgIGlmICghcmVzaXplRWRnZSkgeyBkZWZhdWx0Q3Vyc29yID0gZG9jdW1lbnQuYm9keS5zdHlsZS5jdXJzb3I7IH1cclxuICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLmN1cnNvciA9IGN1cnNvckZvckVkZ2VbZWRnZV07XHJcbiAgICB9IGVsc2UgaWYgKCFlZGdlICYmIHJlc2l6ZUVkZ2UpIHtcclxuICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLmN1cnNvciA9IGRlZmF1bHRDdXJzb3I7XHJcbiAgICB9XHJcblxyXG4gICAgcmVzaXplRWRnZSA9IGVkZ2UgfHwgXCJcIjtcclxufVxyXG5cclxuZnVuY3Rpb24gb25Nb3VzZU1vdmUoZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcclxuICAgIGlmIChjYW5SZXNpemUgJiYgcmVzaXplRWRnZSkge1xyXG4gICAgICAgIC8vIFN0YXJ0IHJlc2l6aW5nLlxyXG4gICAgICAgIHJlc2l6aW5nID0gdHJ1ZTtcclxuICAgICAgICBpbnZva2UoXCJ3YWlsczpyZXNpemU6XCIgKyByZXNpemVFZGdlKTtcclxuICAgIH0gZWxzZSBpZiAoY2FuRHJhZykge1xyXG4gICAgICAgIC8vIFN0YXJ0IGRyYWdnaW5nLlxyXG4gICAgICAgIGRyYWdnaW5nID0gdHJ1ZTtcclxuICAgICAgICBpbnZva2UoXCJ3YWlsczpkcmFnXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChkcmFnZ2luZyB8fCByZXNpemluZykge1xyXG4gICAgICAgIC8vIEVpdGhlciBkcmFnIG9yIHJlc2l6ZSBpcyBvbmdvaW5nLFxyXG4gICAgICAgIC8vIHJlc2V0IHJlYWRpbmVzcyBhbmQgc3RvcCBwcm9jZXNzaW5nLlxyXG4gICAgICAgIGNhbkRyYWcgPSBjYW5SZXNpemUgPSBmYWxzZTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFyZXNpemFibGUgfHwgIUlzV2luZG93cygpKSB7XHJcbiAgICAgICAgaWYgKHJlc2l6ZUVkZ2UpIHsgc2V0UmVzaXplKCk7IH1cclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgcmVzaXplSGFuZGxlSGVpZ2h0ID0gR2V0RmxhZyhcInN5c3RlbS5yZXNpemVIYW5kbGVIZWlnaHRcIikgfHwgNTtcclxuICAgIGNvbnN0IHJlc2l6ZUhhbmRsZVdpZHRoID0gR2V0RmxhZyhcInN5c3RlbS5yZXNpemVIYW5kbGVXaWR0aFwiKSB8fCA1O1xyXG5cclxuICAgIC8vIEV4dHJhIHBpeGVscyBmb3IgdGhlIGNvcm5lciBhcmVhcy5cclxuICAgIGNvbnN0IGNvcm5lckV4dHJhID0gR2V0RmxhZyhcInJlc2l6ZUNvcm5lckV4dHJhXCIpIHx8IDEwO1xyXG5cclxuICAgIGNvbnN0IHJpZ2h0Qm9yZGVyID0gKHdpbmRvdy5vdXRlcldpZHRoIC0gZXZlbnQuY2xpZW50WCkgPCByZXNpemVIYW5kbGVXaWR0aDtcclxuICAgIGNvbnN0IGxlZnRCb3JkZXIgPSBldmVudC5jbGllbnRYIDwgcmVzaXplSGFuZGxlV2lkdGg7XHJcbiAgICBjb25zdCB0b3BCb3JkZXIgPSBldmVudC5jbGllbnRZIDwgcmVzaXplSGFuZGxlSGVpZ2h0O1xyXG4gICAgY29uc3QgYm90dG9tQm9yZGVyID0gKHdpbmRvdy5vdXRlckhlaWdodCAtIGV2ZW50LmNsaWVudFkpIDwgcmVzaXplSGFuZGxlSGVpZ2h0O1xyXG5cclxuICAgIC8vIEFkanVzdCBmb3IgY29ybmVyIGFyZWFzLlxyXG4gICAgY29uc3QgcmlnaHRDb3JuZXIgPSAod2luZG93Lm91dGVyV2lkdGggLSBldmVudC5jbGllbnRYKSA8IChyZXNpemVIYW5kbGVXaWR0aCArIGNvcm5lckV4dHJhKTtcclxuICAgIGNvbnN0IGxlZnRDb3JuZXIgPSBldmVudC5jbGllbnRYIDwgKHJlc2l6ZUhhbmRsZVdpZHRoICsgY29ybmVyRXh0cmEpO1xyXG4gICAgY29uc3QgdG9wQ29ybmVyID0gZXZlbnQuY2xpZW50WSA8IChyZXNpemVIYW5kbGVIZWlnaHQgKyBjb3JuZXJFeHRyYSk7XHJcbiAgICBjb25zdCBib3R0b21Db3JuZXIgPSAod2luZG93Lm91dGVySGVpZ2h0IC0gZXZlbnQuY2xpZW50WSkgPCAocmVzaXplSGFuZGxlSGVpZ2h0ICsgY29ybmVyRXh0cmEpO1xyXG5cclxuICAgIGlmICghbGVmdENvcm5lciAmJiAhdG9wQ29ybmVyICYmICFib3R0b21Db3JuZXIgJiYgIXJpZ2h0Q29ybmVyKSB7XHJcbiAgICAgICAgLy8gT3B0aW1pc2F0aW9uOiBvdXQgb2YgYWxsIGNvcm5lciBhcmVhcyBpbXBsaWVzIG91dCBvZiBib3JkZXJzLlxyXG4gICAgICAgIHNldFJlc2l6ZSgpO1xyXG4gICAgfVxyXG4gICAgLy8gRGV0ZWN0IGNvcm5lcnMuXHJcbiAgICBlbHNlIGlmIChyaWdodENvcm5lciAmJiBib3R0b21Db3JuZXIpIHNldFJlc2l6ZShcInNlLXJlc2l6ZVwiKTtcclxuICAgIGVsc2UgaWYgKGxlZnRDb3JuZXIgJiYgYm90dG9tQ29ybmVyKSBzZXRSZXNpemUoXCJzdy1yZXNpemVcIik7XHJcbiAgICBlbHNlIGlmIChsZWZ0Q29ybmVyICYmIHRvcENvcm5lcikgc2V0UmVzaXplKFwibnctcmVzaXplXCIpO1xyXG4gICAgZWxzZSBpZiAodG9wQ29ybmVyICYmIHJpZ2h0Q29ybmVyKSBzZXRSZXNpemUoXCJuZS1yZXNpemVcIik7XHJcbiAgICAvLyBEZXRlY3QgYm9yZGVycy5cclxuICAgIGVsc2UgaWYgKGxlZnRCb3JkZXIpIHNldFJlc2l6ZShcInctcmVzaXplXCIpO1xyXG4gICAgZWxzZSBpZiAodG9wQm9yZGVyKSBzZXRSZXNpemUoXCJuLXJlc2l6ZVwiKTtcclxuICAgIGVsc2UgaWYgKGJvdHRvbUJvcmRlcikgc2V0UmVzaXplKFwicy1yZXNpemVcIik7XHJcbiAgICBlbHNlIGlmIChyaWdodEJvcmRlcikgc2V0UmVzaXplKFwiZS1yZXNpemVcIik7XHJcbiAgICAvLyBPdXQgb2YgYm9yZGVyIGFyZWEuXHJcbiAgICBlbHNlIHNldFJlc2l6ZSgpO1xyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBuZXdSdW50aW1lQ2FsbGVyLCBvYmplY3ROYW1lcyB9IGZyb20gXCIuL3J1bnRpbWUuanNcIjtcclxuY29uc3QgY2FsbCA9IG5ld1J1bnRpbWVDYWxsZXIob2JqZWN0TmFtZXMuQXBwbGljYXRpb24pO1xyXG5cclxuY29uc3QgSGlkZU1ldGhvZCA9IDA7XHJcbmNvbnN0IFNob3dNZXRob2QgPSAxO1xyXG5jb25zdCBRdWl0TWV0aG9kID0gMjtcclxuXHJcbi8qKlxyXG4gKiBIaWRlcyBhIGNlcnRhaW4gbWV0aG9kIGJ5IGNhbGxpbmcgdGhlIEhpZGVNZXRob2QgZnVuY3Rpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gSGlkZSgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIHJldHVybiBjYWxsKEhpZGVNZXRob2QpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsbHMgdGhlIFNob3dNZXRob2QgYW5kIHJldHVybnMgdGhlIHJlc3VsdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBTaG93KCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgcmV0dXJuIGNhbGwoU2hvd01ldGhvZCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxscyB0aGUgUXVpdE1ldGhvZCB0byB0ZXJtaW5hdGUgdGhlIHByb2dyYW0uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gUXVpdCgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIHJldHVybiBjYWxsKFF1aXRNZXRob2QpO1xyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5pbXBvcnQgeyBDYW5jZWxsYWJsZVByb21pc2UsIHR5cGUgQ2FuY2VsbGFibGVQcm9taXNlV2l0aFJlc29sdmVycyB9IGZyb20gXCIuL2NhbmNlbGxhYmxlLmpzXCI7XHJcbmltcG9ydCB7IG5ld1J1bnRpbWVDYWxsZXIsIG9iamVjdE5hbWVzIH0gZnJvbSBcIi4vcnVudGltZS5qc1wiO1xyXG5pbXBvcnQgeyBuYW5vaWQgfSBmcm9tIFwiLi9uYW5vaWQuanNcIjtcclxuXHJcbi8vIFNldHVwXHJcbndpbmRvdy5fd2FpbHMgPSB3aW5kb3cuX3dhaWxzIHx8IHt9O1xyXG53aW5kb3cuX3dhaWxzLmNhbGxSZXN1bHRIYW5kbGVyID0gcmVzdWx0SGFuZGxlcjtcclxud2luZG93Ll93YWlscy5jYWxsRXJyb3JIYW5kbGVyID0gZXJyb3JIYW5kbGVyO1xyXG5cclxudHlwZSBQcm9taXNlUmVzb2x2ZXJzID0gT21pdDxDYW5jZWxsYWJsZVByb21pc2VXaXRoUmVzb2x2ZXJzPGFueT4sIFwicHJvbWlzZVwiIHwgXCJvbmNhbmNlbGxlZFwiPlxyXG5cclxuY29uc3QgY2FsbCA9IG5ld1J1bnRpbWVDYWxsZXIob2JqZWN0TmFtZXMuQ2FsbCk7XHJcbmNvbnN0IGNhbmNlbENhbGwgPSBuZXdSdW50aW1lQ2FsbGVyKG9iamVjdE5hbWVzLkNhbmNlbENhbGwpO1xyXG5jb25zdCBjYWxsUmVzcG9uc2VzID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2VSZXNvbHZlcnM+KCk7XHJcblxyXG5jb25zdCBDYWxsQmluZGluZyA9IDA7XHJcbmNvbnN0IENhbmNlbE1ldGhvZCA9IDBcclxuXHJcbi8qKlxyXG4gKiBIb2xkcyBhbGwgcmVxdWlyZWQgaW5mb3JtYXRpb24gZm9yIGEgYmluZGluZyBjYWxsLlxyXG4gKiBNYXkgcHJvdmlkZSBlaXRoZXIgYSBtZXRob2QgSUQgb3IgYSBtZXRob2QgbmFtZSwgYnV0IG5vdCBib3RoLlxyXG4gKi9cclxuZXhwb3J0IHR5cGUgQ2FsbE9wdGlvbnMgPSB7XHJcbiAgICAvKiogVGhlIG51bWVyaWMgSUQgb2YgdGhlIGJvdW5kIG1ldGhvZCB0byBjYWxsLiAqL1xyXG4gICAgbWV0aG9kSUQ6IG51bWJlcjtcclxuICAgIC8qKiBUaGUgZnVsbHkgcXVhbGlmaWVkIG5hbWUgb2YgdGhlIGJvdW5kIG1ldGhvZCB0byBjYWxsLiAqL1xyXG4gICAgbWV0aG9kTmFtZT86IG5ldmVyO1xyXG4gICAgLyoqIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgaW50byB0aGUgYm91bmQgbWV0aG9kLiAqL1xyXG4gICAgYXJnczogYW55W107XHJcbn0gfCB7XHJcbiAgICAvKiogVGhlIG51bWVyaWMgSUQgb2YgdGhlIGJvdW5kIG1ldGhvZCB0byBjYWxsLiAqL1xyXG4gICAgbWV0aG9kSUQ/OiBuZXZlcjtcclxuICAgIC8qKiBUaGUgZnVsbHkgcXVhbGlmaWVkIG5hbWUgb2YgdGhlIGJvdW5kIG1ldGhvZCB0byBjYWxsLiAqL1xyXG4gICAgbWV0aG9kTmFtZTogc3RyaW5nO1xyXG4gICAgLyoqIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgaW50byB0aGUgYm91bmQgbWV0aG9kLiAqL1xyXG4gICAgYXJnczogYW55W107XHJcbn07XHJcblxyXG4vKipcclxuICogRXhjZXB0aW9uIGNsYXNzIHRoYXQgd2lsbCBiZSB0aHJvd24gaW4gY2FzZSB0aGUgYm91bmQgbWV0aG9kIHJldHVybnMgYW4gZXJyb3IuXHJcbiAqIFRoZSB2YWx1ZSBvZiB0aGUge0BsaW5rIFJ1bnRpbWVFcnJvciNuYW1lfSBwcm9wZXJ0eSBpcyBcIlJ1bnRpbWVFcnJvclwiLlxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIFJ1bnRpbWVFcnJvciBleHRlbmRzIEVycm9yIHtcclxuICAgIC8qKlxyXG4gICAgICogQ29uc3RydWN0cyBhIG5ldyBSdW50aW1lRXJyb3IgaW5zdGFuY2UuXHJcbiAgICAgKiBAcGFyYW0gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlLlxyXG4gICAgICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25zIHRvIGJlIGZvcndhcmRlZCB0byB0aGUgRXJyb3IgY29uc3RydWN0b3IuXHJcbiAgICAgKi9cclxuICAgIGNvbnN0cnVjdG9yKG1lc3NhZ2U/OiBzdHJpbmcsIG9wdGlvbnM/OiBFcnJvck9wdGlvbnMpIHtcclxuICAgICAgICBzdXBlcihtZXNzYWdlLCBvcHRpb25zKTtcclxuICAgICAgICB0aGlzLm5hbWUgPSBcIlJ1bnRpbWVFcnJvclwiO1xyXG4gICAgfVxyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgcmVzdWx0IG9mIGEgY2FsbCByZXF1ZXN0LlxyXG4gKlxyXG4gKiBAcGFyYW0gaWQgLSBUaGUgaWQgb2YgdGhlIHJlcXVlc3QgdG8gaGFuZGxlIHRoZSByZXN1bHQgZm9yLlxyXG4gKiBAcGFyYW0gZGF0YSAtIFRoZSByZXN1bHQgZGF0YSBvZiB0aGUgcmVxdWVzdC5cclxuICogQHBhcmFtIGlzSlNPTiAtIEluZGljYXRlcyB3aGV0aGVyIHRoZSBkYXRhIGlzIEpTT04gb3Igbm90LlxyXG4gKi9cclxuZnVuY3Rpb24gcmVzdWx0SGFuZGxlcihpZDogc3RyaW5nLCBkYXRhOiBzdHJpbmcsIGlzSlNPTjogYm9vbGVhbik6IHZvaWQge1xyXG4gICAgY29uc3QgcmVzb2x2ZXJzID0gZ2V0QW5kRGVsZXRlUmVzcG9uc2UoaWQpO1xyXG4gICAgaWYgKCFyZXNvbHZlcnMpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFkYXRhKSB7XHJcbiAgICAgICAgcmVzb2x2ZXJzLnJlc29sdmUodW5kZWZpbmVkKTtcclxuICAgIH0gZWxzZSBpZiAoIWlzSlNPTikge1xyXG4gICAgICAgIHJlc29sdmVycy5yZXNvbHZlKGRhdGEpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNvbHZlcnMucmVzb2x2ZShKU09OLnBhcnNlKGRhdGEpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xyXG4gICAgICAgICAgICByZXNvbHZlcnMucmVqZWN0KG5ldyBUeXBlRXJyb3IoXCJjb3VsZCBub3QgcGFyc2UgcmVzdWx0OiBcIiArIGVyci5tZXNzYWdlLCB7IGNhdXNlOiBlcnIgfSkpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGVycm9yIGZyb20gYSBjYWxsIHJlcXVlc3QuXHJcbiAqXHJcbiAqIEBwYXJhbSBpZCAtIFRoZSBpZCBvZiB0aGUgcHJvbWlzZSBoYW5kbGVyLlxyXG4gKiBAcGFyYW0gZGF0YSAtIFRoZSBlcnJvciBkYXRhIHRvIHJlamVjdCB0aGUgcHJvbWlzZSBoYW5kbGVyIHdpdGguXHJcbiAqIEBwYXJhbSBpc0pTT04gLSBJbmRpY2F0ZXMgd2hldGhlciB0aGUgZGF0YSBpcyBKU09OIG9yIG5vdC5cclxuICovXHJcbmZ1bmN0aW9uIGVycm9ySGFuZGxlcihpZDogc3RyaW5nLCBkYXRhOiBzdHJpbmcsIGlzSlNPTjogYm9vbGVhbik6IHZvaWQge1xyXG4gICAgY29uc3QgcmVzb2x2ZXJzID0gZ2V0QW5kRGVsZXRlUmVzcG9uc2UoaWQpO1xyXG4gICAgaWYgKCFyZXNvbHZlcnMpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFpc0pTT04pIHtcclxuICAgICAgICByZXNvbHZlcnMucmVqZWN0KG5ldyBFcnJvcihkYXRhKSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAgIGxldCBlcnJvcjogYW55O1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGVycm9yID0gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xyXG4gICAgICAgICAgICByZXNvbHZlcnMucmVqZWN0KG5ldyBUeXBlRXJyb3IoXCJjb3VsZCBub3QgcGFyc2UgZXJyb3I6IFwiICsgZXJyLm1lc3NhZ2UsIHsgY2F1c2U6IGVyciB9KSk7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxldCBvcHRpb25zOiBFcnJvck9wdGlvbnMgPSB7fTtcclxuICAgICAgICBpZiAoZXJyb3IuY2F1c2UpIHtcclxuICAgICAgICAgICAgb3B0aW9ucy5jYXVzZSA9IGVycm9yLmNhdXNlO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgbGV0IGV4Y2VwdGlvbjtcclxuICAgICAgICBzd2l0Y2ggKGVycm9yLmtpbmQpIHtcclxuICAgICAgICAgICAgY2FzZSBcIlJlZmVyZW5jZUVycm9yXCI6XHJcbiAgICAgICAgICAgICAgICBleGNlcHRpb24gPSBuZXcgUmVmZXJlbmNlRXJyb3IoZXJyb3IubWVzc2FnZSwgb3B0aW9ucyk7XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgY2FzZSBcIlR5cGVFcnJvclwiOlxyXG4gICAgICAgICAgICAgICAgZXhjZXB0aW9uID0gbmV3IFR5cGVFcnJvcihlcnJvci5tZXNzYWdlLCBvcHRpb25zKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlIFwiUnVudGltZUVycm9yXCI6XHJcbiAgICAgICAgICAgICAgICBleGNlcHRpb24gPSBuZXcgUnVudGltZUVycm9yKGVycm9yLm1lc3NhZ2UsIG9wdGlvbnMpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgICAgICBleGNlcHRpb24gPSBuZXcgRXJyb3IoZXJyb3IubWVzc2FnZSwgb3B0aW9ucyk7XHJcbiAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJlc29sdmVycy5yZWplY3QoZXhjZXB0aW9uKTtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmVtb3ZlcyB0aGUgcmVzcG9uc2UgYXNzb2NpYXRlZCB3aXRoIHRoZSBnaXZlbiBJRCBmcm9tIHRoZSBjYWxsUmVzcG9uc2VzIG1hcC5cclxuICpcclxuICogQHBhcmFtIGlkIC0gVGhlIElEIG9mIHRoZSByZXNwb25zZSB0byBiZSByZXRyaWV2ZWQgYW5kIHJlbW92ZWQuXHJcbiAqIEByZXR1cm5zIFRoZSByZXNwb25zZSBvYmplY3QgYXNzb2NpYXRlZCB3aXRoIHRoZSBnaXZlbiBJRCwgaWYgYW55LlxyXG4gKi9cclxuZnVuY3Rpb24gZ2V0QW5kRGVsZXRlUmVzcG9uc2UoaWQ6IHN0cmluZyk6IFByb21pc2VSZXNvbHZlcnMgfCB1bmRlZmluZWQge1xyXG4gICAgY29uc3QgcmVzcG9uc2UgPSBjYWxsUmVzcG9uc2VzLmdldChpZCk7XHJcbiAgICBjYWxsUmVzcG9uc2VzLmRlbGV0ZShpZCk7XHJcbiAgICByZXR1cm4gcmVzcG9uc2U7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZW5lcmF0ZXMgYSB1bmlxdWUgSUQgdXNpbmcgdGhlIG5hbm9pZCBsaWJyYXJ5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyBBIHVuaXF1ZSBJRCB0aGF0IGRvZXMgbm90IGV4aXN0IGluIHRoZSBjYWxsUmVzcG9uc2VzIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIGdlbmVyYXRlSUQoKTogc3RyaW5nIHtcclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICBkbyB7XHJcbiAgICAgICAgcmVzdWx0ID0gbmFub2lkKCk7XHJcbiAgICB9IHdoaWxlIChjYWxsUmVzcG9uc2VzLmhhcyhyZXN1bHQpKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxsIGEgYm91bmQgbWV0aG9kIGFjY29yZGluZyB0byB0aGUgZ2l2ZW4gY2FsbCBvcHRpb25zLlxyXG4gKlxyXG4gKiBJbiBjYXNlIG9mIGZhaWx1cmUsIHRoZSByZXR1cm5lZCBwcm9taXNlIHdpbGwgcmVqZWN0IHdpdGggYW4gZXhjZXB0aW9uXHJcbiAqIGFtb25nIFJlZmVyZW5jZUVycm9yICh1bmtub3duIG1ldGhvZCksIFR5cGVFcnJvciAod3JvbmcgYXJndW1lbnQgY291bnQgb3IgdHlwZSksXHJcbiAqIHtAbGluayBSdW50aW1lRXJyb3J9IChtZXRob2QgcmV0dXJuZWQgYW4gZXJyb3IpLCBvciBvdGhlciAobmV0d29yayBvciBpbnRlcm5hbCBlcnJvcnMpLlxyXG4gKiBUaGUgZXhjZXB0aW9uIG1pZ2h0IGhhdmUgYSBcImNhdXNlXCIgZmllbGQgd2l0aCB0aGUgdmFsdWUgcmV0dXJuZWRcclxuICogYnkgdGhlIGFwcGxpY2F0aW9uLSBvciBzZXJ2aWNlLWxldmVsIGVycm9yIG1hcnNoYWxpbmcgZnVuY3Rpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEEgbWV0aG9kIGNhbGwgZGVzY3JpcHRvci5cclxuICogQHJldHVybnMgVGhlIHJlc3VsdCBvZiB0aGUgY2FsbC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBDYWxsKG9wdGlvbnM6IENhbGxPcHRpb25zKTogQ2FuY2VsbGFibGVQcm9taXNlPGFueT4ge1xyXG4gICAgY29uc3QgaWQgPSBnZW5lcmF0ZUlEKCk7XHJcblxyXG4gICAgY29uc3QgcmVzdWx0ID0gQ2FuY2VsbGFibGVQcm9taXNlLndpdGhSZXNvbHZlcnM8YW55PigpO1xyXG4gICAgY2FsbFJlc3BvbnNlcy5zZXQoaWQsIHsgcmVzb2x2ZTogcmVzdWx0LnJlc29sdmUsIHJlamVjdDogcmVzdWx0LnJlamVjdCB9KTtcclxuXHJcbiAgICBjb25zdCByZXF1ZXN0ID0gY2FsbChDYWxsQmluZGluZywgT2JqZWN0LmFzc2lnbih7IFwiY2FsbC1pZFwiOiBpZCB9LCBvcHRpb25zKSk7XHJcbiAgICBsZXQgcnVubmluZyA9IGZhbHNlO1xyXG5cclxuICAgIHJlcXVlc3QudGhlbigoKSA9PiB7XHJcbiAgICAgICAgcnVubmluZyA9IHRydWU7XHJcbiAgICB9LCAoZXJyKSA9PiB7XHJcbiAgICAgICAgY2FsbFJlc3BvbnNlcy5kZWxldGUoaWQpO1xyXG4gICAgICAgIHJlc3VsdC5yZWplY3QoZXJyKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGNhbmNlbCA9ICgpID0+IHtcclxuICAgICAgICBjYWxsUmVzcG9uc2VzLmRlbGV0ZShpZCk7XHJcbiAgICAgICAgcmV0dXJuIGNhbmNlbENhbGwoQ2FuY2VsTWV0aG9kLCB7XCJjYWxsLWlkXCI6IGlkfSkuY2F0Y2goKGVycikgPT4ge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3Igd2hpbGUgcmVxdWVzdGluZyBiaW5kaW5nIGNhbGwgY2FuY2VsbGF0aW9uOlwiLCBlcnIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfTtcclxuXHJcbiAgICByZXN1bHQub25jYW5jZWxsZWQgPSAoKSA9PiB7XHJcbiAgICAgICAgaWYgKHJ1bm5pbmcpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGNhbmNlbCgpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHJldHVybiByZXF1ZXN0LnRoZW4oY2FuY2VsKTtcclxuICAgICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHJldHVybiByZXN1bHQucHJvbWlzZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGxzIGEgYm91bmQgbWV0aG9kIGJ5IG5hbWUgd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICogU2VlIHtAbGluayBDYWxsfSBmb3IgZGV0YWlscy5cclxuICpcclxuICogQHBhcmFtIG1ldGhvZE5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgbWV0aG9kIGluIHRoZSBmb3JtYXQgJ3BhY2thZ2Uuc3RydWN0Lm1ldGhvZCcuXHJcbiAqIEBwYXJhbSBhcmdzIC0gVGhlIGFyZ3VtZW50cyB0byBwYXNzIHRvIHRoZSBtZXRob2QuXHJcbiAqIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIG1ldGhvZCBjYWxsLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIEJ5TmFtZShtZXRob2ROYW1lOiBzdHJpbmcsIC4uLmFyZ3M6IGFueVtdKTogQ2FuY2VsbGFibGVQcm9taXNlPGFueT4ge1xyXG4gICAgcmV0dXJuIENhbGwoeyBtZXRob2ROYW1lLCBhcmdzIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsbHMgYSBtZXRob2QgYnkgaXRzIG51bWVyaWMgSUQgd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICogU2VlIHtAbGluayBDYWxsfSBmb3IgZGV0YWlscy5cclxuICpcclxuICogQHBhcmFtIG1ldGhvZElEIC0gVGhlIElEIG9mIHRoZSBtZXRob2QgdG8gY2FsbC5cclxuICogQHBhcmFtIGFyZ3MgLSBUaGUgYXJndW1lbnRzIHRvIHBhc3MgdG8gdGhlIG1ldGhvZC5cclxuICogQHJldHVybiBUaGUgcmVzdWx0IG9mIHRoZSBtZXRob2QgY2FsbC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBCeUlEKG1ldGhvZElEOiBudW1iZXIsIC4uLmFyZ3M6IGFueVtdKTogQ2FuY2VsbGFibGVQcm9taXNlPGFueT4ge1xyXG4gICAgcmV0dXJuIENhbGwoeyBtZXRob2RJRCwgYXJncyB9KTtcclxufVxyXG4iLCAiLy8gU291cmNlOiBodHRwczovL2dpdGh1Yi5jb20vaW5zcGVjdC1qcy9pcy1jYWxsYWJsZVxyXG5cclxuLy8gVGhlIE1JVCBMaWNlbnNlIChNSVQpXHJcbi8vXHJcbi8vIENvcHlyaWdodCAoYykgMjAxNSBKb3JkYW4gSGFyYmFuZFxyXG4vL1xyXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XHJcbi8vIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlIFwiU29mdHdhcmVcIiksIHRvIGRlYWxcclxuLy8gaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0c1xyXG4vLyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsXHJcbi8vIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpc1xyXG4vLyBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOlxyXG4vL1xyXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpbiBhbGxcclxuLy8gY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cclxuLy9cclxuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxyXG4vLyBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcclxuLy8gRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFXHJcbi8vIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcclxuLy8gTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcclxuLy8gT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEVcclxuLy8gU09GVFdBUkUuXHJcblxyXG52YXIgZm5Ub1N0ciA9IEZ1bmN0aW9uLnByb3RvdHlwZS50b1N0cmluZztcclxudmFyIHJlZmxlY3RBcHBseTogdHlwZW9mIFJlZmxlY3QuYXBwbHkgfCBmYWxzZSB8IG51bGwgPSB0eXBlb2YgUmVmbGVjdCA9PT0gJ29iamVjdCcgJiYgUmVmbGVjdCAhPT0gbnVsbCAmJiBSZWZsZWN0LmFwcGx5O1xyXG52YXIgYmFkQXJyYXlMaWtlOiBhbnk7XHJcbnZhciBpc0NhbGxhYmxlTWFya2VyOiBhbnk7XHJcbmlmICh0eXBlb2YgcmVmbGVjdEFwcGx5ID09PSAnZnVuY3Rpb24nICYmIHR5cGVvZiBPYmplY3QuZGVmaW5lUHJvcGVydHkgPT09ICdmdW5jdGlvbicpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgICAgYmFkQXJyYXlMaWtlID0gT2JqZWN0LmRlZmluZVByb3BlcnR5KHt9LCAnbGVuZ3RoJywge1xyXG4gICAgICAgICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IGlzQ2FsbGFibGVNYXJrZXI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgICBpc0NhbGxhYmxlTWFya2VyID0ge307XHJcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXRocm93LWxpdGVyYWxcclxuICAgICAgICByZWZsZWN0QXBwbHkoZnVuY3Rpb24gKCkgeyB0aHJvdyA0MjsgfSwgbnVsbCwgYmFkQXJyYXlMaWtlKTtcclxuICAgIH0gY2F0Y2ggKF8pIHtcclxuICAgICAgICBpZiAoXyAhPT0gaXNDYWxsYWJsZU1hcmtlcikge1xyXG4gICAgICAgICAgICByZWZsZWN0QXBwbHkgPSBudWxsO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufSBlbHNlIHtcclxuICAgIHJlZmxlY3RBcHBseSA9IG51bGw7XHJcbn1cclxuXHJcbnZhciBjb25zdHJ1Y3RvclJlZ2V4ID0gL15cXHMqY2xhc3NcXGIvO1xyXG52YXIgaXNFUzZDbGFzc0ZuID0gZnVuY3Rpb24gaXNFUzZDbGFzc0Z1bmN0aW9uKHZhbHVlOiBhbnkpOiBib29sZWFuIHtcclxuICAgIHRyeSB7XHJcbiAgICAgICAgdmFyIGZuU3RyID0gZm5Ub1N0ci5jYWxsKHZhbHVlKTtcclxuICAgICAgICByZXR1cm4gY29uc3RydWN0b3JSZWdleC50ZXN0KGZuU3RyKTtcclxuICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICByZXR1cm4gZmFsc2U7IC8vIG5vdCBhIGZ1bmN0aW9uXHJcbiAgICB9XHJcbn07XHJcblxyXG52YXIgdHJ5RnVuY3Rpb25PYmplY3QgPSBmdW5jdGlvbiB0cnlGdW5jdGlvblRvU3RyKHZhbHVlOiBhbnkpOiBib29sZWFuIHtcclxuICAgIHRyeSB7XHJcbiAgICAgICAgaWYgKGlzRVM2Q2xhc3NGbih2YWx1ZSkpIHsgcmV0dXJuIGZhbHNlOyB9XHJcbiAgICAgICAgZm5Ub1N0ci5jYWxsKHZhbHVlKTtcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbn07XHJcbnZhciB0b1N0ciA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7XHJcbnZhciBvYmplY3RDbGFzcyA9ICdbb2JqZWN0IE9iamVjdF0nO1xyXG52YXIgZm5DbGFzcyA9ICdbb2JqZWN0IEZ1bmN0aW9uXSc7XHJcbnZhciBnZW5DbGFzcyA9ICdbb2JqZWN0IEdlbmVyYXRvckZ1bmN0aW9uXSc7XHJcbnZhciBkZGFDbGFzcyA9ICdbb2JqZWN0IEhUTUxBbGxDb2xsZWN0aW9uXSc7IC8vIElFIDExXHJcbnZhciBkZGFDbGFzczIgPSAnW29iamVjdCBIVE1MIGRvY3VtZW50LmFsbCBjbGFzc10nO1xyXG52YXIgZGRhQ2xhc3MzID0gJ1tvYmplY3QgSFRNTENvbGxlY3Rpb25dJzsgLy8gSUUgOS0xMFxyXG52YXIgaGFzVG9TdHJpbmdUYWcgPSB0eXBlb2YgU3ltYm9sID09PSAnZnVuY3Rpb24nICYmICEhU3ltYm9sLnRvU3RyaW5nVGFnOyAvLyBiZXR0ZXI6IHVzZSBgaGFzLXRvc3RyaW5ndGFnYFxyXG5cclxudmFyIGlzSUU2OCA9ICEoMCBpbiBbLF0pOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXNwYXJzZS1hcnJheXMsIGNvbW1hLXNwYWNpbmdcclxuXHJcbnZhciBpc0REQTogKHZhbHVlOiBhbnkpID0+IGJvb2xlYW4gPSBmdW5jdGlvbiBpc0RvY3VtZW50RG90QWxsKCkgeyByZXR1cm4gZmFsc2U7IH07XHJcbmlmICh0eXBlb2YgZG9jdW1lbnQgPT09ICdvYmplY3QnKSB7XHJcbiAgICAvLyBGaXJlZm94IDMgY2Fub25pY2FsaXplcyBEREEgdG8gdW5kZWZpbmVkIHdoZW4gaXQncyBub3QgYWNjZXNzZWQgZGlyZWN0bHlcclxuICAgIHZhciBhbGwgPSBkb2N1bWVudC5hbGw7XHJcbiAgICBpZiAodG9TdHIuY2FsbChhbGwpID09PSB0b1N0ci5jYWxsKGRvY3VtZW50LmFsbCkpIHtcclxuICAgICAgICBpc0REQSA9IGZ1bmN0aW9uIGlzRG9jdW1lbnREb3RBbGwodmFsdWUpIHtcclxuICAgICAgICAgICAgLyogZ2xvYmFscyBkb2N1bWVudDogZmFsc2UgKi9cclxuICAgICAgICAgICAgLy8gaW4gSUUgNi04LCB0eXBlb2YgZG9jdW1lbnQuYWxsIGlzIFwib2JqZWN0XCIgYW5kIGl0J3MgdHJ1dGh5XHJcbiAgICAgICAgICAgIGlmICgoaXNJRTY4IHx8ICF2YWx1ZSkgJiYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3VuZGVmaW5lZCcgfHwgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JykpIHtcclxuICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdmFyIHN0ciA9IHRvU3RyLmNhbGwodmFsdWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiAoXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0ciA9PT0gZGRhQ2xhc3NcclxuICAgICAgICAgICAgICAgICAgICAgICAgfHwgc3RyID09PSBkZGFDbGFzczJcclxuICAgICAgICAgICAgICAgICAgICAgICAgfHwgc3RyID09PSBkZGFDbGFzczMgLy8gb3BlcmEgMTIuMTZcclxuICAgICAgICAgICAgICAgICAgICAgICAgfHwgc3RyID09PSBvYmplY3RDbGFzcyAvLyBJRSA2LThcclxuICAgICAgICAgICAgICAgICAgICApICYmIHZhbHVlKCcnKSA9PSBudWxsOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIGVxZXFlcVxyXG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkgeyAvKiovIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfTtcclxuICAgIH1cclxufVxyXG5cclxuZnVuY3Rpb24gaXNDYWxsYWJsZVJlZkFwcGx5PFQ+KHZhbHVlOiBUIHwgdW5rbm93bik6IHZhbHVlIGlzICguLi5hcmdzOiBhbnlbXSkgPT4gYW55ICB7XHJcbiAgICBpZiAoaXNEREEodmFsdWUpKSB7IHJldHVybiB0cnVlOyB9XHJcbiAgICBpZiAoIXZhbHVlKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ2Z1bmN0aW9uJyAmJiB0eXBlb2YgdmFsdWUgIT09ICdvYmplY3QnKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgdHJ5IHtcclxuICAgICAgICAocmVmbGVjdEFwcGx5IGFzIGFueSkodmFsdWUsIG51bGwsIGJhZEFycmF5TGlrZSk7XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgaWYgKGUgIT09IGlzQ2FsbGFibGVNYXJrZXIpIHsgcmV0dXJuIGZhbHNlOyB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gIWlzRVM2Q2xhc3NGbih2YWx1ZSkgJiYgdHJ5RnVuY3Rpb25PYmplY3QodmFsdWUpO1xyXG59XHJcblxyXG5mdW5jdGlvbiBpc0NhbGxhYmxlTm9SZWZBcHBseTxUPih2YWx1ZTogVCB8IHVua25vd24pOiB2YWx1ZSBpcyAoLi4uYXJnczogYW55W10pID0+IGFueSB7XHJcbiAgICBpZiAoaXNEREEodmFsdWUpKSB7IHJldHVybiB0cnVlOyB9XHJcbiAgICBpZiAoIXZhbHVlKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ2Z1bmN0aW9uJyAmJiB0eXBlb2YgdmFsdWUgIT09ICdvYmplY3QnKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgaWYgKGhhc1RvU3RyaW5nVGFnKSB7IHJldHVybiB0cnlGdW5jdGlvbk9iamVjdCh2YWx1ZSk7IH1cclxuICAgIGlmIChpc0VTNkNsYXNzRm4odmFsdWUpKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgdmFyIHN0ckNsYXNzID0gdG9TdHIuY2FsbCh2YWx1ZSk7XHJcbiAgICBpZiAoc3RyQ2xhc3MgIT09IGZuQ2xhc3MgJiYgc3RyQ2xhc3MgIT09IGdlbkNsYXNzICYmICEoL15cXFtvYmplY3QgSFRNTC8pLnRlc3Qoc3RyQ2xhc3MpKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgcmV0dXJuIHRyeUZ1bmN0aW9uT2JqZWN0KHZhbHVlKTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHJlZmxlY3RBcHBseSA/IGlzQ2FsbGFibGVSZWZBcHBseSA6IGlzQ2FsbGFibGVOb1JlZkFwcGx5O1xyXG4iLCAiLypcclxuIF9cdCAgIF9fXHQgIF8gX19cclxufCB8XHQgLyAvX19fIF8oXykgL19fX19cclxufCB8IC98IC8gLyBfXyBgLyAvIC8gX19fL1xyXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcclxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXHJcblRoZSBlbGVjdHJvbiBhbHRlcm5hdGl2ZSBmb3IgR29cclxuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxyXG4qL1xyXG5cclxuaW1wb3J0IGlzQ2FsbGFibGUgZnJvbSBcIi4vY2FsbGFibGUuanNcIjtcclxuXHJcbi8qKlxyXG4gKiBFeGNlcHRpb24gY2xhc3MgdGhhdCB3aWxsIGJlIHVzZWQgYXMgcmVqZWN0aW9uIHJlYXNvblxyXG4gKiBpbiBjYXNlIGEge0BsaW5rIENhbmNlbGxhYmxlUHJvbWlzZX0gaXMgY2FuY2VsbGVkIHN1Y2Nlc3NmdWxseS5cclxuICpcclxuICogVGhlIHZhbHVlIG9mIHRoZSB7QGxpbmsgbmFtZX0gcHJvcGVydHkgaXMgdGhlIHN0cmluZyBgXCJDYW5jZWxFcnJvclwiYC5cclxuICogVGhlIHZhbHVlIG9mIHRoZSB7QGxpbmsgY2F1c2V9IHByb3BlcnR5IGlzIHRoZSBjYXVzZSBwYXNzZWQgdG8gdGhlIGNhbmNlbCBtZXRob2QsIGlmIGFueS5cclxuICovXHJcbmV4cG9ydCBjbGFzcyBDYW5jZWxFcnJvciBleHRlbmRzIEVycm9yIHtcclxuICAgIC8qKlxyXG4gICAgICogQ29uc3RydWN0cyBhIG5ldyBgQ2FuY2VsRXJyb3JgIGluc3RhbmNlLlxyXG4gICAgICogQHBhcmFtIG1lc3NhZ2UgLSBUaGUgZXJyb3IgbWVzc2FnZS5cclxuICAgICAqIEBwYXJhbSBvcHRpb25zIC0gT3B0aW9ucyB0byBiZSBmb3J3YXJkZWQgdG8gdGhlIEVycm9yIGNvbnN0cnVjdG9yLlxyXG4gICAgICovXHJcbiAgICBjb25zdHJ1Y3RvcihtZXNzYWdlPzogc3RyaW5nLCBvcHRpb25zPzogRXJyb3JPcHRpb25zKSB7XHJcbiAgICAgICAgc3VwZXIobWVzc2FnZSwgb3B0aW9ucyk7XHJcbiAgICAgICAgdGhpcy5uYW1lID0gXCJDYW5jZWxFcnJvclwiO1xyXG4gICAgfVxyXG59XHJcblxyXG4vKipcclxuICogRXhjZXB0aW9uIGNsYXNzIHRoYXQgd2lsbCBiZSByZXBvcnRlZCBhcyBhbiB1bmhhbmRsZWQgcmVqZWN0aW9uXHJcbiAqIGluIGNhc2UgYSB7QGxpbmsgQ2FuY2VsbGFibGVQcm9taXNlfSByZWplY3RzIGFmdGVyIGJlaW5nIGNhbmNlbGxlZCxcclxuICogb3Igd2hlbiB0aGUgYG9uY2FuY2VsbGVkYCBjYWxsYmFjayB0aHJvd3Mgb3IgcmVqZWN0cy5cclxuICpcclxuICogVGhlIHZhbHVlIG9mIHRoZSB7QGxpbmsgbmFtZX0gcHJvcGVydHkgaXMgdGhlIHN0cmluZyBgXCJDYW5jZWxsZWRSZWplY3Rpb25FcnJvclwiYC5cclxuICogVGhlIHZhbHVlIG9mIHRoZSB7QGxpbmsgY2F1c2V9IHByb3BlcnR5IGlzIHRoZSByZWFzb24gdGhlIHByb21pc2UgcmVqZWN0ZWQgd2l0aC5cclxuICpcclxuICogQmVjYXVzZSB0aGUgb3JpZ2luYWwgcHJvbWlzZSB3YXMgY2FuY2VsbGVkLFxyXG4gKiBhIHdyYXBwZXIgcHJvbWlzZSB3aWxsIGJlIHBhc3NlZCB0byB0aGUgdW5oYW5kbGVkIHJlamVjdGlvbiBsaXN0ZW5lciBpbnN0ZWFkLlxyXG4gKiBUaGUge0BsaW5rIHByb21pc2V9IHByb3BlcnR5IGhvbGRzIGEgcmVmZXJlbmNlIHRvIHRoZSBvcmlnaW5hbCBwcm9taXNlLlxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIENhbmNlbGxlZFJlamVjdGlvbkVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gICAgLyoqXHJcbiAgICAgKiBIb2xkcyBhIHJlZmVyZW5jZSB0byB0aGUgcHJvbWlzZSB0aGF0IHdhcyBjYW5jZWxsZWQgYW5kIHRoZW4gcmVqZWN0ZWQuXHJcbiAgICAgKi9cclxuICAgIHByb21pc2U6IENhbmNlbGxhYmxlUHJvbWlzZTx1bmtub3duPjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIENvbnN0cnVjdHMgYSBuZXcgYENhbmNlbGxlZFJlamVjdGlvbkVycm9yYCBpbnN0YW5jZS5cclxuICAgICAqIEBwYXJhbSBwcm9taXNlIC0gVGhlIHByb21pc2UgdGhhdCBjYXVzZWQgdGhlIGVycm9yIG9yaWdpbmFsbHkuXHJcbiAgICAgKiBAcGFyYW0gcmVhc29uIC0gVGhlIHJlamVjdGlvbiByZWFzb24uXHJcbiAgICAgKiBAcGFyYW0gaW5mbyAtIEFuIG9wdGlvbmFsIGluZm9ybWF0aXZlIG1lc3NhZ2Ugc3BlY2lmeWluZyB0aGUgY2lyY3Vtc3RhbmNlcyBpbiB3aGljaCB0aGUgZXJyb3Igd2FzIHRocm93bi5cclxuICAgICAqICAgICAgICAgICAgICAgRGVmYXVsdHMgdG8gdGhlIHN0cmluZyBgXCJVbmhhbmRsZWQgcmVqZWN0aW9uIGluIGNhbmNlbGxlZCBwcm9taXNlLlwiYC5cclxuICAgICAqL1xyXG4gICAgY29uc3RydWN0b3IocHJvbWlzZTogQ2FuY2VsbGFibGVQcm9taXNlPHVua25vd24+LCByZWFzb24/OiBhbnksIGluZm8/OiBzdHJpbmcpIHtcclxuICAgICAgICBzdXBlcigoaW5mbyA/PyBcIlVuaGFuZGxlZCByZWplY3Rpb24gaW4gY2FuY2VsbGVkIHByb21pc2UuXCIpICsgXCIgUmVhc29uOiBcIiArIGVycm9yTWVzc2FnZShyZWFzb24pLCB7IGNhdXNlOiByZWFzb24gfSk7XHJcbiAgICAgICAgdGhpcy5wcm9taXNlID0gcHJvbWlzZTtcclxuICAgICAgICB0aGlzLm5hbWUgPSBcIkNhbmNlbGxlZFJlamVjdGlvbkVycm9yXCI7XHJcbiAgICB9XHJcbn1cclxuXHJcbnR5cGUgQ2FuY2VsbGFibGVQcm9taXNlUmVzb2x2ZXI8VD4gPSAodmFsdWU6IFQgfCBQcm9taXNlTGlrZTxUPiB8IENhbmNlbGxhYmxlUHJvbWlzZUxpa2U8VD4pID0+IHZvaWQ7XHJcbnR5cGUgQ2FuY2VsbGFibGVQcm9taXNlUmVqZWN0b3IgPSAocmVhc29uPzogYW55KSA9PiB2b2lkO1xyXG50eXBlIENhbmNlbGxhYmxlUHJvbWlzZUNhbmNlbGxlciA9IChjYXVzZT86IGFueSkgPT4gdm9pZCB8IFByb21pc2VMaWtlPHZvaWQ+O1xyXG50eXBlIENhbmNlbGxhYmxlUHJvbWlzZUV4ZWN1dG9yPFQ+ID0gKHJlc29sdmU6IENhbmNlbGxhYmxlUHJvbWlzZVJlc29sdmVyPFQ+LCByZWplY3Q6IENhbmNlbGxhYmxlUHJvbWlzZVJlamVjdG9yKSA9PiB2b2lkO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBDYW5jZWxsYWJsZVByb21pc2VMaWtlPFQ+IHtcclxuICAgIHRoZW48VFJlc3VsdDEgPSBULCBUUmVzdWx0MiA9IG5ldmVyPihvbmZ1bGZpbGxlZD86ICgodmFsdWU6IFQpID0+IFRSZXN1bHQxIHwgUHJvbWlzZUxpa2U8VFJlc3VsdDE+IHwgQ2FuY2VsbGFibGVQcm9taXNlTGlrZTxUUmVzdWx0MT4pIHwgdW5kZWZpbmVkIHwgbnVsbCwgb25yZWplY3RlZD86ICgocmVhc29uOiBhbnkpID0+IFRSZXN1bHQyIHwgUHJvbWlzZUxpa2U8VFJlc3VsdDI+IHwgQ2FuY2VsbGFibGVQcm9taXNlTGlrZTxUUmVzdWx0Mj4pIHwgdW5kZWZpbmVkIHwgbnVsbCk6IENhbmNlbGxhYmxlUHJvbWlzZUxpa2U8VFJlc3VsdDEgfCBUUmVzdWx0Mj47XHJcbiAgICBjYW5jZWwoY2F1c2U/OiBhbnkpOiB2b2lkIHwgUHJvbWlzZUxpa2U8dm9pZD47XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBhIGNhbmNlbGxhYmxlIHByb21pc2UgYWxvbmcgd2l0aCBpdHMgcmVzb2x1dGlvbiBtZXRob2RzLlxyXG4gKiBUaGUgYG9uY2FuY2VsbGVkYCBmaWVsZCB3aWxsIGJlIG51bGwgaW5pdGlhbGx5IGJ1dCBtYXkgYmUgc2V0IHRvIHByb3ZpZGUgYSBjdXN0b20gY2FuY2VsbGF0aW9uIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGludGVyZmFjZSBDYW5jZWxsYWJsZVByb21pc2VXaXRoUmVzb2x2ZXJzPFQ+IHtcclxuICAgIHByb21pc2U6IENhbmNlbGxhYmxlUHJvbWlzZTxUPjtcclxuICAgIHJlc29sdmU6IENhbmNlbGxhYmxlUHJvbWlzZVJlc29sdmVyPFQ+O1xyXG4gICAgcmVqZWN0OiBDYW5jZWxsYWJsZVByb21pc2VSZWplY3RvcjtcclxuICAgIG9uY2FuY2VsbGVkOiBDYW5jZWxsYWJsZVByb21pc2VDYW5jZWxsZXIgfCBudWxsO1xyXG59XHJcblxyXG5pbnRlcmZhY2UgQ2FuY2VsbGFibGVQcm9taXNlU3RhdGUge1xyXG4gICAgcmVhZG9ubHkgcm9vdDogQ2FuY2VsbGFibGVQcm9taXNlU3RhdGU7XHJcbiAgICByZXNvbHZpbmc6IGJvb2xlYW47XHJcbiAgICBzZXR0bGVkOiBib29sZWFuO1xyXG4gICAgcmVhc29uPzogQ2FuY2VsRXJyb3I7XHJcbn1cclxuXHJcbi8vIFByaXZhdGUgZmllbGQgbmFtZXMuXHJcbmNvbnN0IGJhcnJpZXJTeW0gPSBTeW1ib2woXCJiYXJyaWVyXCIpO1xyXG5jb25zdCBjYW5jZWxJbXBsU3ltID0gU3ltYm9sKFwiY2FuY2VsSW1wbFwiKTtcclxuY29uc3Qgc3BlY2llcyA9IFN5bWJvbC5zcGVjaWVzID8/IFN5bWJvbChcInNwZWNpZXNQb2x5ZmlsbFwiKTtcclxuXHJcbi8qKlxyXG4gKiBBIHByb21pc2Ugd2l0aCBhbiBhdHRhY2hlZCBtZXRob2QgZm9yIGNhbmNlbGxpbmcgbG9uZy1ydW5uaW5nIG9wZXJhdGlvbnMgKHNlZSB7QGxpbmsgQ2FuY2VsbGFibGVQcm9taXNlI2NhbmNlbH0pLlxyXG4gKiBDYW5jZWxsYXRpb24gY2FuIG9wdGlvbmFsbHkgYmUgYm91bmQgdG8gYW4ge0BsaW5rIEFib3J0U2lnbmFsfVxyXG4gKiBmb3IgYmV0dGVyIGNvbXBvc2FiaWxpdHkgKHNlZSB7QGxpbmsgQ2FuY2VsbGFibGVQcm9taXNlI2NhbmNlbE9ufSkuXHJcbiAqXHJcbiAqIENhbmNlbGxpbmcgYSBwZW5kaW5nIHByb21pc2Ugd2lsbCByZXN1bHQgaW4gYW4gaW1tZWRpYXRlIHJlamVjdGlvblxyXG4gKiB3aXRoIGFuIGluc3RhbmNlIG9mIHtAbGluayBDYW5jZWxFcnJvcn0gYXMgcmVhc29uLFxyXG4gKiBidXQgd2hvZXZlciBzdGFydGVkIHRoZSBwcm9taXNlIHdpbGwgYmUgcmVzcG9uc2libGVcclxuICogZm9yIGFjdHVhbGx5IGFib3J0aW5nIHRoZSB1bmRlcmx5aW5nIG9wZXJhdGlvbi5cclxuICogVG8gdGhpcyBwdXJwb3NlLCB0aGUgY29uc3RydWN0b3IgYW5kIGFsbCBjaGFpbmluZyBtZXRob2RzXHJcbiAqIGFjY2VwdCBvcHRpb25hbCBjYW5jZWxsYXRpb24gY2FsbGJhY2tzLlxyXG4gKlxyXG4gKiBJZiBhIGBDYW5jZWxsYWJsZVByb21pc2VgIHN0aWxsIHJlc29sdmVzIGFmdGVyIGhhdmluZyBiZWVuIGNhbmNlbGxlZCxcclxuICogdGhlIHJlc3VsdCB3aWxsIGJlIGRpc2NhcmRlZC4gSWYgaXQgcmVqZWN0cywgdGhlIHJlYXNvblxyXG4gKiB3aWxsIGJlIHJlcG9ydGVkIGFzIGFuIHVuaGFuZGxlZCByZWplY3Rpb24sXHJcbiAqIHdyYXBwZWQgaW4gYSB7QGxpbmsgQ2FuY2VsbGVkUmVqZWN0aW9uRXJyb3J9IGluc3RhbmNlLlxyXG4gKiBUbyBmYWNpbGl0YXRlIHRoZSBoYW5kbGluZyBvZiBjYW5jZWxsYXRpb24gcmVxdWVzdHMsXHJcbiAqIGNhbmNlbGxlZCBgQ2FuY2VsbGFibGVQcm9taXNlYHMgd2lsbCBfbm90XyByZXBvcnQgdW5oYW5kbGVkIGBDYW5jZWxFcnJvcmBzXHJcbiAqIHdob3NlIGBjYXVzZWAgZmllbGQgaXMgdGhlIHNhbWUgYXMgdGhlIG9uZSB3aXRoIHdoaWNoIHRoZSBjdXJyZW50IHByb21pc2Ugd2FzIGNhbmNlbGxlZC5cclxuICpcclxuICogQWxsIHVzdWFsIHByb21pc2UgbWV0aG9kcyBhcmUgZGVmaW5lZCBhbmQgcmV0dXJuIGEgYENhbmNlbGxhYmxlUHJvbWlzZWBcclxuICogd2hvc2UgY2FuY2VsIG1ldGhvZCB3aWxsIGNhbmNlbCB0aGUgcGFyZW50IG9wZXJhdGlvbiBhcyB3ZWxsLCBwcm9wYWdhdGluZyB0aGUgY2FuY2VsbGF0aW9uIHJlYXNvblxyXG4gKiB1cHdhcmRzIHRocm91Z2ggcHJvbWlzZSBjaGFpbnMuXHJcbiAqIENvbnZlcnNlbHksIGNhbmNlbGxpbmcgYSBwcm9taXNlIHdpbGwgbm90IGF1dG9tYXRpY2FsbHkgY2FuY2VsIGRlcGVuZGVudCBwcm9taXNlcyBkb3duc3RyZWFtOlxyXG4gKiBgYGB0c1xyXG4gKiBsZXQgcm9vdCA9IG5ldyBDYW5jZWxsYWJsZVByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4geyAuLi4gfSk7XHJcbiAqIGxldCBjaGlsZDEgPSByb290LnRoZW4oKCkgPT4geyAuLi4gfSk7XHJcbiAqIGxldCBjaGlsZDIgPSBjaGlsZDEudGhlbigoKSA9PiB7IC4uLiB9KTtcclxuICogbGV0IGNoaWxkMyA9IHJvb3QuY2F0Y2goKCkgPT4geyAuLi4gfSk7XHJcbiAqIGNoaWxkMS5jYW5jZWwoKTsgLy8gQ2FuY2VscyBjaGlsZDEgYW5kIHJvb3QsIGJ1dCBub3QgY2hpbGQyIG9yIGNoaWxkM1xyXG4gKiBgYGBcclxuICogQ2FuY2VsbGluZyBhIHByb21pc2UgdGhhdCBoYXMgYWxyZWFkeSBzZXR0bGVkIGlzIHNhZmUgYW5kIGhhcyBubyBjb25zZXF1ZW5jZS5cclxuICpcclxuICogVGhlIGBjYW5jZWxgIG1ldGhvZCByZXR1cm5zIGEgcHJvbWlzZSB0aGF0IF9hbHdheXMgZnVsZmlsbHNfXHJcbiAqIGFmdGVyIHRoZSB3aG9sZSBjaGFpbiBoYXMgcHJvY2Vzc2VkIHRoZSBjYW5jZWwgcmVxdWVzdFxyXG4gKiBhbmQgYWxsIGF0dGFjaGVkIGNhbGxiYWNrcyB1cCB0byB0aGF0IG1vbWVudCBoYXZlIHJ1bi5cclxuICpcclxuICogQWxsIEVTMjAyNCBwcm9taXNlIG1ldGhvZHMgKHN0YXRpYyBhbmQgaW5zdGFuY2UpIGFyZSBkZWZpbmVkIG9uIENhbmNlbGxhYmxlUHJvbWlzZSxcclxuICogYnV0IGFjdHVhbCBhdmFpbGFiaWxpdHkgbWF5IHZhcnkgd2l0aCBPUy93ZWJ2aWV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEluIGxpbmUgd2l0aCB0aGUgcHJvcG9zYWwgYXQgaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtcm0tYnVpbHRpbi1zdWJjbGFzc2luZyxcclxuICogYENhbmNlbGxhYmxlUHJvbWlzZWAgZG9lcyBub3Qgc3VwcG9ydCB0cmFuc3BhcmVudCBzdWJjbGFzc2luZy5cclxuICogRXh0ZW5kZXJzIHNob3VsZCB0YWtlIGNhcmUgdG8gcHJvdmlkZSB0aGVpciBvd24gbWV0aG9kIGltcGxlbWVudGF0aW9ucy5cclxuICogVGhpcyBtaWdodCBiZSByZWNvbnNpZGVyZWQgaW4gY2FzZSB0aGUgcHJvcG9zYWwgaXMgcmV0aXJlZC5cclxuICpcclxuICogQ2FuY2VsbGFibGVQcm9taXNlIGlzIGEgd3JhcHBlciBhcm91bmQgdGhlIERPTSBQcm9taXNlIG9iamVjdFxyXG4gKiBhbmQgaXMgY29tcGxpYW50IHdpdGggdGhlIFtQcm9taXNlcy9BKyBzcGVjaWZpY2F0aW9uXShodHRwczovL3Byb21pc2VzYXBsdXMuY29tLylcclxuICogKGl0IHBhc3NlcyB0aGUgW2NvbXBsaWFuY2Ugc3VpdGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9wcm9taXNlcy1hcGx1cy9wcm9taXNlcy10ZXN0cykpXHJcbiAqIGlmIHNvIGlzIHRoZSB1bmRlcmx5aW5nIGltcGxlbWVudGF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIENhbmNlbGxhYmxlUHJvbWlzZTxUPiBleHRlbmRzIFByb21pc2U8VD4gaW1wbGVtZW50cyBQcm9taXNlTGlrZTxUPiwgQ2FuY2VsbGFibGVQcm9taXNlTGlrZTxUPiB7XHJcbiAgICAvLyBQcml2YXRlIGZpZWxkcy5cclxuICAgIC8qKiBAaW50ZXJuYWwgKi9cclxuICAgIHByaXZhdGUgW2JhcnJpZXJTeW1dITogUGFydGlhbDxQcm9taXNlV2l0aFJlc29sdmVyczx2b2lkPj4gfCBudWxsO1xyXG4gICAgLyoqIEBpbnRlcm5hbCAqL1xyXG4gICAgcHJpdmF0ZSByZWFkb25seSBbY2FuY2VsSW1wbFN5bV0hOiAocmVhc29uOiBDYW5jZWxFcnJvcikgPT4gdm9pZCB8IFByb21pc2VMaWtlPHZvaWQ+O1xyXG5cclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyBgQ2FuY2VsbGFibGVQcm9taXNlYC5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gZXhlY3V0b3IgLSBBIGNhbGxiYWNrIHVzZWQgdG8gaW5pdGlhbGl6ZSB0aGUgcHJvbWlzZS4gVGhpcyBjYWxsYmFjayBpcyBwYXNzZWQgdHdvIGFyZ3VtZW50czpcclxuICAgICAqICAgICAgICAgICAgICAgICAgIGEgYHJlc29sdmVgIGNhbGxiYWNrIHVzZWQgdG8gcmVzb2x2ZSB0aGUgcHJvbWlzZSB3aXRoIGEgdmFsdWVcclxuICAgICAqICAgICAgICAgICAgICAgICAgIG9yIHRoZSByZXN1bHQgb2YgYW5vdGhlciBwcm9taXNlIChwb3NzaWJseSBjYW5jZWxsYWJsZSksXHJcbiAgICAgKiAgICAgICAgICAgICAgICAgICBhbmQgYSBgcmVqZWN0YCBjYWxsYmFjayB1c2VkIHRvIHJlamVjdCB0aGUgcHJvbWlzZSB3aXRoIGEgcHJvdmlkZWQgcmVhc29uIG9yIGVycm9yLlxyXG4gICAgICogICAgICAgICAgICAgICAgICAgSWYgdGhlIHZhbHVlIHByb3ZpZGVkIHRvIHRoZSBgcmVzb2x2ZWAgY2FsbGJhY2sgaXMgYSB0aGVuYWJsZSBfYW5kXyBjYW5jZWxsYWJsZSBvYmplY3RcclxuICAgICAqICAgICAgICAgICAgICAgICAgIChpdCBoYXMgYSBgdGhlbmAgX2FuZF8gYSBgY2FuY2VsYCBtZXRob2QpLFxyXG4gICAgICogICAgICAgICAgICAgICAgICAgY2FuY2VsbGF0aW9uIHJlcXVlc3RzIHdpbGwgYmUgZm9yd2FyZGVkIHRvIHRoYXQgb2JqZWN0IGFuZCB0aGUgb25jYW5jZWxsZWQgd2lsbCBub3QgYmUgaW52b2tlZCBhbnltb3JlLlxyXG4gICAgICogICAgICAgICAgICAgICAgICAgSWYgYW55IG9uZSBvZiB0aGUgdHdvIGNhbGxiYWNrcyBpcyBjYWxsZWQgX2FmdGVyXyB0aGUgcHJvbWlzZSBoYXMgYmVlbiBjYW5jZWxsZWQsXHJcbiAgICAgKiAgICAgICAgICAgICAgICAgICB0aGUgcHJvdmlkZWQgdmFsdWVzIHdpbGwgYmUgY2FuY2VsbGVkIGFuZCByZXNvbHZlZCBhcyB1c3VhbCxcclxuICAgICAqICAgICAgICAgICAgICAgICAgIGJ1dCB0aGVpciByZXN1bHRzIHdpbGwgYmUgZGlzY2FyZGVkLlxyXG4gICAgICogICAgICAgICAgICAgICAgICAgSG93ZXZlciwgaWYgdGhlIHJlc29sdXRpb24gcHJvY2VzcyB1bHRpbWF0ZWx5IGVuZHMgdXAgaW4gYSByZWplY3Rpb25cclxuICAgICAqICAgICAgICAgICAgICAgICAgIHRoYXQgaXMgbm90IGR1ZSB0byBjYW5jZWxsYXRpb24sIHRoZSByZWplY3Rpb24gcmVhc29uXHJcbiAgICAgKiAgICAgICAgICAgICAgICAgICB3aWxsIGJlIHdyYXBwZWQgaW4gYSB7QGxpbmsgQ2FuY2VsbGVkUmVqZWN0aW9uRXJyb3J9XHJcbiAgICAgKiAgICAgICAgICAgICAgICAgICBhbmQgYnViYmxlZCB1cCBhcyBhbiB1bmhhbmRsZWQgcmVqZWN0aW9uLlxyXG4gICAgICogQHBhcmFtIG9uY2FuY2VsbGVkIC0gSXQgaXMgdGhlIGNhbGxlcidzIHJlc3BvbnNpYmlsaXR5IHRvIGVuc3VyZSB0aGF0IGFueSBvcGVyYXRpb25cclxuICAgICAqICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ZWQgYnkgdGhlIGV4ZWN1dG9yIGlzIHByb3Blcmx5IGhhbHRlZCB1cG9uIGNhbmNlbGxhdGlvbi5cclxuICAgICAqICAgICAgICAgICAgICAgICAgICAgIFRoaXMgb3B0aW9uYWwgY2FsbGJhY2sgY2FuIGJlIHVzZWQgdG8gdGhhdCBwdXJwb3NlLlxyXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgSXQgd2lsbCBiZSBjYWxsZWQgX3N5bmNocm9ub3VzbHlfIHdpdGggYSBjYW5jZWxsYXRpb24gY2F1c2VcclxuICAgICAqICAgICAgICAgICAgICAgICAgICAgIHdoZW4gY2FuY2VsbGF0aW9uIGlzIHJlcXVlc3RlZCwgX2FmdGVyXyB0aGUgcHJvbWlzZSBoYXMgYWxyZWFkeSByZWplY3RlZFxyXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgd2l0aCBhIHtAbGluayBDYW5jZWxFcnJvcn0sIGJ1dCBfYmVmb3JlX1xyXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgYW55IHtAbGluayB0aGVufS97QGxpbmsgY2F0Y2h9L3tAbGluayBmaW5hbGx5fSBjYWxsYmFjayBydW5zLlxyXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgSWYgdGhlIGNhbGxiYWNrIHJldHVybnMgYSB0aGVuYWJsZSwgdGhlIHByb21pc2UgcmV0dXJuZWQgZnJvbSB7QGxpbmsgY2FuY2VsfVxyXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgd2lsbCBvbmx5IGZ1bGZpbGwgYWZ0ZXIgdGhlIGZvcm1lciBoYXMgc2V0dGxlZC5cclxuICAgICAqICAgICAgICAgICAgICAgICAgICAgIFVuaGFuZGxlZCBleGNlcHRpb25zIG9yIHJlamVjdGlvbnMgZnJvbSB0aGUgY2FsbGJhY2sgd2lsbCBiZSB3cmFwcGVkXHJcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICBpbiBhIHtAbGluayBDYW5jZWxsZWRSZWplY3Rpb25FcnJvcn0gYW5kIGJ1YmJsZWQgdXAgYXMgdW5oYW5kbGVkIHJlamVjdGlvbnMuXHJcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICBJZiB0aGUgYHJlc29sdmVgIGNhbGxiYWNrIGlzIGNhbGxlZCBiZWZvcmUgY2FuY2VsbGF0aW9uIHdpdGggYSBjYW5jZWxsYWJsZSBwcm9taXNlLFxyXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgY2FuY2VsbGF0aW9uIHJlcXVlc3RzIG9uIHRoaXMgcHJvbWlzZSB3aWxsIGJlIGRpdmVydGVkIHRvIHRoYXQgcHJvbWlzZSxcclxuICAgICAqICAgICAgICAgICAgICAgICAgICAgIGFuZCB0aGUgb3JpZ2luYWwgYG9uY2FuY2VsbGVkYCBjYWxsYmFjayB3aWxsIGJlIGRpc2NhcmRlZC5cclxuICAgICAqL1xyXG4gICAgY29uc3RydWN0b3IoZXhlY3V0b3I6IENhbmNlbGxhYmxlUHJvbWlzZUV4ZWN1dG9yPFQ+LCBvbmNhbmNlbGxlZD86IENhbmNlbGxhYmxlUHJvbWlzZUNhbmNlbGxlcikge1xyXG4gICAgICAgIGxldCByZXNvbHZlITogKHZhbHVlOiBUIHwgUHJvbWlzZUxpa2U8VD4pID0+IHZvaWQ7XHJcbiAgICAgICAgbGV0IHJlamVjdCE6IChyZWFzb24/OiBhbnkpID0+IHZvaWQ7XHJcbiAgICAgICAgc3VwZXIoKHJlcywgcmVqKSA9PiB7IHJlc29sdmUgPSByZXM7IHJlamVjdCA9IHJlajsgfSk7XHJcblxyXG4gICAgICAgIGlmICgodGhpcy5jb25zdHJ1Y3RvciBhcyBhbnkpW3NwZWNpZXNdICE9PSBQcm9taXNlKSB7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5jZWxsYWJsZVByb21pc2UgZG9lcyBub3Qgc3VwcG9ydCB0cmFuc3BhcmVudCBzdWJjbGFzc2luZy4gUGxlYXNlIHJlZnJhaW4gZnJvbSBvdmVycmlkaW5nIHRoZSBbU3ltYm9sLnNwZWNpZXNdIHN0YXRpYyBwcm9wZXJ0eS5cIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBsZXQgcHJvbWlzZTogQ2FuY2VsbGFibGVQcm9taXNlV2l0aFJlc29sdmVyczxUPiA9IHtcclxuICAgICAgICAgICAgcHJvbWlzZTogdGhpcyxcclxuICAgICAgICAgICAgcmVzb2x2ZSxcclxuICAgICAgICAgICAgcmVqZWN0LFxyXG4gICAgICAgICAgICBnZXQgb25jYW5jZWxsZWQoKSB7IHJldHVybiBvbmNhbmNlbGxlZCA/PyBudWxsOyB9LFxyXG4gICAgICAgICAgICBzZXQgb25jYW5jZWxsZWQoY2IpIHsgb25jYW5jZWxsZWQgPSBjYiA/PyB1bmRlZmluZWQ7IH1cclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBjb25zdCBzdGF0ZTogQ2FuY2VsbGFibGVQcm9taXNlU3RhdGUgPSB7XHJcbiAgICAgICAgICAgIGdldCByb290KCkgeyByZXR1cm4gc3RhdGU7IH0sXHJcbiAgICAgICAgICAgIHJlc29sdmluZzogZmFsc2UsXHJcbiAgICAgICAgICAgIHNldHRsZWQ6IGZhbHNlXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgLy8gU2V0dXAgY2FuY2VsbGF0aW9uIHN5c3RlbS5cclxuICAgICAgICB2b2lkIE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKHRoaXMsIHtcclxuICAgICAgICAgICAgW2JhcnJpZXJTeW1dOiB7XHJcbiAgICAgICAgICAgICAgICBjb25maWd1cmFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgZW51bWVyYWJsZTogZmFsc2UsXHJcbiAgICAgICAgICAgICAgICB3cml0YWJsZTogdHJ1ZSxcclxuICAgICAgICAgICAgICAgIHZhbHVlOiBudWxsXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIFtjYW5jZWxJbXBsU3ltXToge1xyXG4gICAgICAgICAgICAgICAgY29uZmlndXJhYmxlOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgIGVudW1lcmFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgd3JpdGFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgdmFsdWU6IGNhbmNlbGxlckZvcihwcm9taXNlLCBzdGF0ZSlcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBSdW4gdGhlIGFjdHVhbCBleGVjdXRvci5cclxuICAgICAgICBjb25zdCByZWplY3RvciA9IHJlamVjdG9yRm9yKHByb21pc2UsIHN0YXRlKTtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBleGVjdXRvcihyZXNvbHZlckZvcihwcm9taXNlLCBzdGF0ZSksIHJlamVjdG9yKTtcclxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgICAgICAgICAgaWYgKHN0YXRlLnJlc29sdmluZykge1xyXG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJVbmhhbmRsZWQgZXhjZXB0aW9uIGluIENhbmNlbGxhYmxlUHJvbWlzZSBleGVjdXRvci5cIiwgZXJyKTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHJlamVjdG9yKGVycik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDYW5jZWxzIGltbWVkaWF0ZWx5IHRoZSBleGVjdXRpb24gb2YgdGhlIG9wZXJhdGlvbiBhc3NvY2lhdGVkIHdpdGggdGhpcyBwcm9taXNlLlxyXG4gICAgICogVGhlIHByb21pc2UgcmVqZWN0cyB3aXRoIGEge0BsaW5rIENhbmNlbEVycm9yfSBpbnN0YW5jZSBhcyByZWFzb24sXHJcbiAgICAgKiB3aXRoIHRoZSB7QGxpbmsgQ2FuY2VsRXJyb3IjY2F1c2V9IHByb3BlcnR5IHNldCB0byB0aGUgZ2l2ZW4gYXJndW1lbnQsIGlmIGFueS5cclxuICAgICAqXHJcbiAgICAgKiBIYXMgbm8gZWZmZWN0IGlmIGNhbGxlZCBhZnRlciB0aGUgcHJvbWlzZSBoYXMgYWxyZWFkeSBzZXR0bGVkO1xyXG4gICAgICogcmVwZWF0ZWQgY2FsbHMgaW4gcGFydGljdWxhciBhcmUgc2FmZSwgYnV0IG9ubHkgdGhlIGZpcnN0IG9uZVxyXG4gICAgICogd2lsbCBzZXQgdGhlIGNhbmNlbGxhdGlvbiBjYXVzZS5cclxuICAgICAqXHJcbiAgICAgKiBUaGUgYENhbmNlbEVycm9yYCBleGNlcHRpb24gX25lZWQgbm90XyBiZSBoYW5kbGVkIGV4cGxpY2l0bHkgX29uIHRoZSBwcm9taXNlcyB0aGF0IGFyZSBiZWluZyBjYW5jZWxsZWQ6X1xyXG4gICAgICogY2FuY2VsbGluZyBhIHByb21pc2Ugd2l0aCBubyBhdHRhY2hlZCByZWplY3Rpb24gaGFuZGxlciBkb2VzIG5vdCB0cmlnZ2VyIGFuIHVuaGFuZGxlZCByZWplY3Rpb24gZXZlbnQuXHJcbiAgICAgKiBUaGVyZWZvcmUsIHRoZSBmb2xsb3dpbmcgaWRpb21zIGFyZSBhbGwgZXF1YWxseSBjb3JyZWN0OlxyXG4gICAgICogYGBgdHNcclxuICAgICAqIG5ldyBDYW5jZWxsYWJsZVByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4geyAuLi4gfSkuY2FuY2VsKCk7XHJcbiAgICAgKiBuZXcgQ2FuY2VsbGFibGVQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsgLi4uIH0pLnRoZW4oLi4uKS5jYW5jZWwoKTtcclxuICAgICAqIG5ldyBDYW5jZWxsYWJsZVByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4geyAuLi4gfSkudGhlbiguLi4pLmNhdGNoKC4uLikuY2FuY2VsKCk7XHJcbiAgICAgKiBgYGBcclxuICAgICAqIFdoZW5ldmVyIHNvbWUgY2FuY2VsbGVkIHByb21pc2UgaW4gYSBjaGFpbiByZWplY3RzIHdpdGggYSBgQ2FuY2VsRXJyb3JgXHJcbiAgICAgKiB3aXRoIHRoZSBzYW1lIGNhbmNlbGxhdGlvbiBjYXVzZSBhcyBpdHNlbGYsIHRoZSBlcnJvciB3aWxsIGJlIGRpc2NhcmRlZCBzaWxlbnRseS5cclxuICAgICAqIEhvd2V2ZXIsIHRoZSBgQ2FuY2VsRXJyb3JgIF93aWxsIHN0aWxsIGJlIGRlbGl2ZXJlZF8gdG8gYWxsIGF0dGFjaGVkIHJlamVjdGlvbiBoYW5kbGVyc1xyXG4gICAgICogYWRkZWQgYnkge0BsaW5rIHRoZW59IGFuZCByZWxhdGVkIG1ldGhvZHM6XHJcbiAgICAgKiBgYGB0c1xyXG4gICAgICogbGV0IGNhbmNlbGxhYmxlID0gbmV3IENhbmNlbGxhYmxlUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7IC4uLiB9KTtcclxuICAgICAqIGNhbmNlbGxhYmxlLnRoZW4oKCkgPT4geyAuLi4gfSkuY2F0Y2goY29uc29sZS5sb2cpO1xyXG4gICAgICogY2FuY2VsbGFibGUuY2FuY2VsKCk7IC8vIEEgQ2FuY2VsRXJyb3IgaXMgcHJpbnRlZCB0byB0aGUgY29uc29sZS5cclxuICAgICAqIGBgYFxyXG4gICAgICogSWYgdGhlIGBDYW5jZWxFcnJvcmAgaXMgbm90IGhhbmRsZWQgZG93bnN0cmVhbSBieSB0aGUgdGltZSBpdCByZWFjaGVzXHJcbiAgICAgKiBhIF9ub24tY2FuY2VsbGVkXyBwcm9taXNlLCBpdCBfd2lsbF8gdHJpZ2dlciBhbiB1bmhhbmRsZWQgcmVqZWN0aW9uIGV2ZW50LFxyXG4gICAgICoganVzdCBsaWtlIG5vcm1hbCByZWplY3Rpb25zIHdvdWxkOlxyXG4gICAgICogYGBgdHNcclxuICAgICAqIGxldCBjYW5jZWxsYWJsZSA9IG5ldyBDYW5jZWxsYWJsZVByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4geyAuLi4gfSk7XHJcbiAgICAgKiBsZXQgY2hhaW5lZCA9IGNhbmNlbGxhYmxlLnRoZW4oKCkgPT4geyAuLi4gfSkudGhlbigoKSA9PiB7IC4uLiB9KTsgLy8gTm8gY2F0Y2guLi5cclxuICAgICAqIGNhbmNlbGxhYmxlLmNhbmNlbCgpOyAvLyBVbmhhbmRsZWQgcmVqZWN0aW9uIGV2ZW50IG9uIGNoYWluZWQhXHJcbiAgICAgKiBgYGBcclxuICAgICAqIFRoZXJlZm9yZSwgaXQgaXMgaW1wb3J0YW50IHRvIGVpdGhlciBjYW5jZWwgd2hvbGUgcHJvbWlzZSBjaGFpbnMgZnJvbSB0aGVpciB0YWlsLFxyXG4gICAgICogYXMgc2hvd24gaW4gdGhlIGNvcnJlY3QgaWRpb21zIGFib3ZlLCBvciB0YWtlIGNhcmUgb2YgaGFuZGxpbmcgZXJyb3JzIGV2ZXJ5d2hlcmUuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgQSBjYW5jZWxsYWJsZSBwcm9taXNlIHRoYXQgX2Z1bGZpbGxzXyBhZnRlciB0aGUgY2FuY2VsIGNhbGxiYWNrIChpZiBhbnkpXHJcbiAgICAgKiBhbmQgYWxsIGhhbmRsZXJzIGF0dGFjaGVkIHVwIHRvIHRoZSBjYWxsIHRvIGNhbmNlbCBoYXZlIHJ1bi5cclxuICAgICAqIElmIHRoZSBjYW5jZWwgY2FsbGJhY2sgcmV0dXJucyBhIHRoZW5hYmxlLCB0aGUgcHJvbWlzZSByZXR1cm5lZCBieSBgY2FuY2VsYFxyXG4gICAgICogd2lsbCBhbHNvIHdhaXQgZm9yIHRoYXQgdGhlbmFibGUgdG8gc2V0dGxlLlxyXG4gICAgICogVGhpcyBlbmFibGVzIGNhbGxlcnMgdG8gd2FpdCBmb3IgdGhlIGNhbmNlbGxlZCBvcGVyYXRpb24gdG8gdGVybWluYXRlXHJcbiAgICAgKiB3aXRob3V0IGJlaW5nIGZvcmNlZCB0byBoYW5kbGUgcG90ZW50aWFsIGVycm9ycyBhdCB0aGUgY2FsbCBzaXRlLlxyXG4gICAgICogYGBgdHNcclxuICAgICAqIGNhbmNlbGxhYmxlLmNhbmNlbCgpLnRoZW4oKCkgPT4ge1xyXG4gICAgICogICAgIC8vIENsZWFudXAgZmluaXNoZWQsIGl0J3Mgc2FmZSB0byBkbyBzb21ldGhpbmcgZWxzZS5cclxuICAgICAqIH0sIChlcnIpID0+IHtcclxuICAgICAqICAgICAvLyBVbnJlYWNoYWJsZTogdGhlIHByb21pc2UgcmV0dXJuZWQgZnJvbSBjYW5jZWwgd2lsbCBuZXZlciByZWplY3QuXHJcbiAgICAgKiB9KTtcclxuICAgICAqIGBgYFxyXG4gICAgICogTm90ZSB0aGF0IHRoZSByZXR1cm5lZCBwcm9taXNlIHdpbGwgX25vdF8gaGFuZGxlIGltcGxpY2l0bHkgYW55IHJlamVjdGlvblxyXG4gICAgICogdGhhdCBtaWdodCBoYXZlIG9jY3VycmVkIGFscmVhZHkgaW4gdGhlIGNhbmNlbGxlZCBjaGFpbi5cclxuICAgICAqIEl0IHdpbGwganVzdCB0cmFjayB3aGV0aGVyIHJlZ2lzdGVyZWQgaGFuZGxlcnMgaGF2ZSBiZWVuIGV4ZWN1dGVkIG9yIG5vdC5cclxuICAgICAqIFRoZXJlZm9yZSwgdW5oYW5kbGVkIHJlamVjdGlvbnMgd2lsbCBuZXZlciBiZSBzaWxlbnRseSBoYW5kbGVkIGJ5IGNhbGxpbmcgY2FuY2VsLlxyXG4gICAgICovXHJcbiAgICBjYW5jZWwoY2F1c2U/OiBhbnkpOiBDYW5jZWxsYWJsZVByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIHJldHVybiBuZXcgQ2FuY2VsbGFibGVQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XHJcbiAgICAgICAgICAgIC8vIElOVkFSSUFOVDogdGhlIHJlc3VsdCBvZiB0aGlzW2NhbmNlbEltcGxTeW1dIGFuZCB0aGUgYmFycmllciBkbyBub3QgZXZlciByZWplY3QuXHJcbiAgICAgICAgICAgIC8vIFVuZm9ydHVuYXRlbHkgbWFjT1MgSGlnaCBTaWVycmEgZG9lcyBub3Qgc3VwcG9ydCBQcm9taXNlLmFsbFNldHRsZWQuXHJcbiAgICAgICAgICAgIFByb21pc2UuYWxsKFtcclxuICAgICAgICAgICAgICAgIHRoaXNbY2FuY2VsSW1wbFN5bV0obmV3IENhbmNlbEVycm9yKFwiUHJvbWlzZSBjYW5jZWxsZWQuXCIsIHsgY2F1c2UgfSkpLFxyXG4gICAgICAgICAgICAgICAgY3VycmVudEJhcnJpZXIodGhpcylcclxuICAgICAgICAgICAgXSkudGhlbigoKSA9PiByZXNvbHZlKCksICgpID0+IHJlc29sdmUoKSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBCaW5kcyBwcm9taXNlIGNhbmNlbGxhdGlvbiB0byB0aGUgYWJvcnQgZXZlbnQgb2YgdGhlIGdpdmVuIHtAbGluayBBYm9ydFNpZ25hbH0uXHJcbiAgICAgKiBJZiB0aGUgc2lnbmFsIGhhcyBhbHJlYWR5IGFib3J0ZWQsIHRoZSBwcm9taXNlIHdpbGwgYmUgY2FuY2VsbGVkIGltbWVkaWF0ZWx5LlxyXG4gICAgICogV2hlbiBlaXRoZXIgY29uZGl0aW9uIGlzIHZlcmlmaWVkLCB0aGUgY2FuY2VsbGF0aW9uIGNhdXNlIHdpbGwgYmUgc2V0XHJcbiAgICAgKiB0byB0aGUgc2lnbmFsJ3MgYWJvcnQgcmVhc29uIChzZWUge0BsaW5rIEFib3J0U2lnbmFsI3JlYXNvbn0pLlxyXG4gICAgICpcclxuICAgICAqIEhhcyBubyBlZmZlY3QgaWYgY2FsbGVkIChvciBpZiB0aGUgc2lnbmFsIGFib3J0cykgX2FmdGVyXyB0aGUgcHJvbWlzZSBoYXMgYWxyZWFkeSBzZXR0bGVkLlxyXG4gICAgICogT25seSB0aGUgZmlyc3Qgc2lnbmFsIHRvIGFib3J0IHdpbGwgc2V0IHRoZSBjYW5jZWxsYXRpb24gY2F1c2UuXHJcbiAgICAgKlxyXG4gICAgICogRm9yIG1vcmUgZGV0YWlscyBhYm91dCB0aGUgY2FuY2VsbGF0aW9uIHByb2Nlc3MsXHJcbiAgICAgKiBzZWUge0BsaW5rIGNhbmNlbH0gYW5kIHRoZSBgQ2FuY2VsbGFibGVQcm9taXNlYCBjb25zdHJ1Y3Rvci5cclxuICAgICAqXHJcbiAgICAgKiBUaGlzIG1ldGhvZCBlbmFibGVzIGBhd2FpdGBpbmcgY2FuY2VsbGFibGUgcHJvbWlzZXMgd2l0aG91dCBoYXZpbmdcclxuICAgICAqIHRvIHN0b3JlIHRoZW0gZm9yIGZ1dHVyZSBjYW5jZWxsYXRpb24sIGUuZy46XHJcbiAgICAgKiBgYGB0c1xyXG4gICAgICogYXdhaXQgbG9uZ1J1bm5pbmdPcGVyYXRpb24oKS5jYW5jZWxPbihzaWduYWwpO1xyXG4gICAgICogYGBgXHJcbiAgICAgKiBpbnN0ZWFkIG9mOlxyXG4gICAgICogYGBgdHNcclxuICAgICAqIGxldCBwcm9taXNlVG9CZUNhbmNlbGxlZCA9IGxvbmdSdW5uaW5nT3BlcmF0aW9uKCk7XHJcbiAgICAgKiBhd2FpdCBwcm9taXNlVG9CZUNhbmNlbGxlZDtcclxuICAgICAqIGBgYFxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIFRoaXMgcHJvbWlzZSwgZm9yIG1ldGhvZCBjaGFpbmluZy5cclxuICAgICAqL1xyXG4gICAgY2FuY2VsT24oc2lnbmFsOiBBYm9ydFNpZ25hbCk6IENhbmNlbGxhYmxlUHJvbWlzZTxUPiB7XHJcbiAgICAgICAgaWYgKHNpZ25hbC5hYm9ydGVkKSB7XHJcbiAgICAgICAgICAgIHZvaWQgdGhpcy5jYW5jZWwoc2lnbmFsLnJlYXNvbilcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBzaWduYWwuYWRkRXZlbnRMaXN0ZW5lcignYWJvcnQnLCAoKSA9PiB2b2lkIHRoaXMuY2FuY2VsKHNpZ25hbC5yZWFzb24pLCB7Y2FwdHVyZTogdHJ1ZX0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBBdHRhY2hlcyBjYWxsYmFja3MgZm9yIHRoZSByZXNvbHV0aW9uIGFuZC9vciByZWplY3Rpb24gb2YgdGhlIGBDYW5jZWxsYWJsZVByb21pc2VgLlxyXG4gICAgICpcclxuICAgICAqIFRoZSBvcHRpb25hbCBgb25jYW5jZWxsZWRgIGFyZ3VtZW50IHdpbGwgYmUgaW52b2tlZCB3aGVuIHRoZSByZXR1cm5lZCBwcm9taXNlIGlzIGNhbmNlbGxlZCxcclxuICAgICAqIHdpdGggdGhlIHNhbWUgc2VtYW50aWNzIGFzIHRoZSBgb25jYW5jZWxsZWRgIGFyZ3VtZW50IG9mIHRoZSBjb25zdHJ1Y3Rvci5cclxuICAgICAqIFdoZW4gdGhlIHBhcmVudCBwcm9taXNlIHJlamVjdHMgb3IgaXMgY2FuY2VsbGVkLCB0aGUgYG9ucmVqZWN0ZWRgIGNhbGxiYWNrIHdpbGwgcnVuLFxyXG4gICAgICogX2V2ZW4gYWZ0ZXIgdGhlIHJldHVybmVkIHByb21pc2UgaGFzIGJlZW4gY2FuY2VsbGVkOl9cclxuICAgICAqIGluIHRoYXQgY2FzZSwgc2hvdWxkIGl0IHJlamVjdCBvciB0aHJvdywgdGhlIHJlYXNvbiB3aWxsIGJlIHdyYXBwZWRcclxuICAgICAqIGluIGEge0BsaW5rIENhbmNlbGxlZFJlamVjdGlvbkVycm9yfSBhbmQgYnViYmxlZCB1cCBhcyBhbiB1bmhhbmRsZWQgcmVqZWN0aW9uLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBvbmZ1bGZpbGxlZCBUaGUgY2FsbGJhY2sgdG8gZXhlY3V0ZSB3aGVuIHRoZSBQcm9taXNlIGlzIHJlc29sdmVkLlxyXG4gICAgICogQHBhcmFtIG9ucmVqZWN0ZWQgVGhlIGNhbGxiYWNrIHRvIGV4ZWN1dGUgd2hlbiB0aGUgUHJvbWlzZSBpcyByZWplY3RlZC5cclxuICAgICAqIEByZXR1cm5zIEEgYENhbmNlbGxhYmxlUHJvbWlzZWAgZm9yIHRoZSBjb21wbGV0aW9uIG9mIHdoaWNoZXZlciBjYWxsYmFjayBpcyBleGVjdXRlZC5cclxuICAgICAqIFRoZSByZXR1cm5lZCBwcm9taXNlIGlzIGhvb2tlZCB1cCB0byBwcm9wYWdhdGUgY2FuY2VsbGF0aW9uIHJlcXVlc3RzIHVwIHRoZSBjaGFpbiwgYnV0IG5vdCBkb3duOlxyXG4gICAgICpcclxuICAgICAqICAgLSBpZiB0aGUgcGFyZW50IHByb21pc2UgaXMgY2FuY2VsbGVkLCB0aGUgYG9ucmVqZWN0ZWRgIGhhbmRsZXIgd2lsbCBiZSBpbnZva2VkIHdpdGggYSBgQ2FuY2VsRXJyb3JgXHJcbiAgICAgKiAgICAgYW5kIHRoZSByZXR1cm5lZCBwcm9taXNlIF93aWxsIHJlc29sdmUgcmVndWxhcmx5XyB3aXRoIGl0cyByZXN1bHQ7XHJcbiAgICAgKiAgIC0gY29udmVyc2VseSwgaWYgdGhlIHJldHVybmVkIHByb21pc2UgaXMgY2FuY2VsbGVkLCBfdGhlIHBhcmVudCBwcm9taXNlIGlzIGNhbmNlbGxlZCB0b287X1xyXG4gICAgICogICAgIHRoZSBgb25yZWplY3RlZGAgaGFuZGxlciB3aWxsIHN0aWxsIGJlIGludm9rZWQgd2l0aCB0aGUgcGFyZW50J3MgYENhbmNlbEVycm9yYCxcclxuICAgICAqICAgICBidXQgaXRzIHJlc3VsdCB3aWxsIGJlIGRpc2NhcmRlZFxyXG4gICAgICogICAgIGFuZCB0aGUgcmV0dXJuZWQgcHJvbWlzZSB3aWxsIHJlamVjdCB3aXRoIGEgYENhbmNlbEVycm9yYCBhcyB3ZWxsLlxyXG4gICAgICpcclxuICAgICAqIFRoZSBwcm9taXNlIHJldHVybmVkIGZyb20ge0BsaW5rIGNhbmNlbH0gd2lsbCBmdWxmaWxsIG9ubHkgYWZ0ZXIgYWxsIGF0dGFjaGVkIGhhbmRsZXJzXHJcbiAgICAgKiB1cCB0aGUgZW50aXJlIHByb21pc2UgY2hhaW4gaGF2ZSBiZWVuIHJ1bi5cclxuICAgICAqXHJcbiAgICAgKiBJZiBlaXRoZXIgY2FsbGJhY2sgcmV0dXJucyBhIGNhbmNlbGxhYmxlIHByb21pc2UsXHJcbiAgICAgKiBjYW5jZWxsYXRpb24gcmVxdWVzdHMgd2lsbCBiZSBkaXZlcnRlZCB0byBpdCxcclxuICAgICAqIGFuZCB0aGUgc3BlY2lmaWVkIGBvbmNhbmNlbGxlZGAgY2FsbGJhY2sgd2lsbCBiZSBkaXNjYXJkZWQuXHJcbiAgICAgKi9cclxuICAgIHRoZW48VFJlc3VsdDEgPSBULCBUUmVzdWx0MiA9IG5ldmVyPihvbmZ1bGZpbGxlZD86ICgodmFsdWU6IFQpID0+IFRSZXN1bHQxIHwgUHJvbWlzZUxpa2U8VFJlc3VsdDE+IHwgQ2FuY2VsbGFibGVQcm9taXNlTGlrZTxUUmVzdWx0MT4pIHwgdW5kZWZpbmVkIHwgbnVsbCwgb25yZWplY3RlZD86ICgocmVhc29uOiBhbnkpID0+IFRSZXN1bHQyIHwgUHJvbWlzZUxpa2U8VFJlc3VsdDI+IHwgQ2FuY2VsbGFibGVQcm9taXNlTGlrZTxUUmVzdWx0Mj4pIHwgdW5kZWZpbmVkIHwgbnVsbCwgb25jYW5jZWxsZWQ/OiBDYW5jZWxsYWJsZVByb21pc2VDYW5jZWxsZXIpOiBDYW5jZWxsYWJsZVByb21pc2U8VFJlc3VsdDEgfCBUUmVzdWx0Mj4ge1xyXG4gICAgICAgIGlmICghKHRoaXMgaW5zdGFuY2VvZiBDYW5jZWxsYWJsZVByb21pc2UpKSB7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5jZWxsYWJsZVByb21pc2UucHJvdG90eXBlLnRoZW4gY2FsbGVkIG9uIGFuIGludmFsaWQgb2JqZWN0LlwiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIE5PVEU6IFR5cGVTY3JpcHQncyBidWlsdC1pbiB0eXBlIGZvciB0aGVuIGlzIGJyb2tlbixcclxuICAgICAgICAvLyBhcyBpdCBhbGxvd3Mgc3BlY2lmeWluZyBhbiBhcmJpdHJhcnkgVFJlc3VsdDEgIT0gVCBldmVuIHdoZW4gb25mdWxmaWxsZWQgaXMgbm90IGEgZnVuY3Rpb24uXHJcbiAgICAgICAgLy8gV2UgY2Fubm90IGZpeCBpdCBpZiB3ZSB3YW50IHRvIENhbmNlbGxhYmxlUHJvbWlzZSB0byBpbXBsZW1lbnQgUHJvbWlzZUxpa2U8VD4uXHJcblxyXG4gICAgICAgIGlmICghaXNDYWxsYWJsZShvbmZ1bGZpbGxlZCkpIHsgb25mdWxmaWxsZWQgPSBpZGVudGl0eSBhcyBhbnk7IH1cclxuICAgICAgICBpZiAoIWlzQ2FsbGFibGUob25yZWplY3RlZCkpIHsgb25yZWplY3RlZCA9IHRocm93ZXI7IH1cclxuXHJcbiAgICAgICAgaWYgKG9uZnVsZmlsbGVkID09PSBpZGVudGl0eSAmJiBvbnJlamVjdGVkID09IHRocm93ZXIpIHtcclxuICAgICAgICAgICAgLy8gU2hvcnRjdXQgZm9yIHRyaXZpYWwgYXJndW1lbnRzLlxyXG4gICAgICAgICAgICByZXR1cm4gbmV3IENhbmNlbGxhYmxlUHJvbWlzZSgocmVzb2x2ZSkgPT4gcmVzb2x2ZSh0aGlzIGFzIGFueSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc3QgYmFycmllcjogUGFydGlhbDxQcm9taXNlV2l0aFJlc29sdmVyczx2b2lkPj4gPSB7fTtcclxuICAgICAgICB0aGlzW2JhcnJpZXJTeW1dID0gYmFycmllcjtcclxuXHJcbiAgICAgICAgcmV0dXJuIG5ldyBDYW5jZWxsYWJsZVByb21pc2U8VFJlc3VsdDEgfCBUUmVzdWx0Mj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICB2b2lkIHN1cGVyLnRoZW4oXHJcbiAgICAgICAgICAgICAgICAodmFsdWUpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpc1tiYXJyaWVyU3ltXSA9PT0gYmFycmllcikgeyB0aGlzW2JhcnJpZXJTeW1dID0gbnVsbDsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGJhcnJpZXIucmVzb2x2ZT8uKCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUob25mdWxmaWxsZWQhKHZhbHVlKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChlcnIpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgICAocmVhc29uPykgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzW2JhcnJpZXJTeW1dID09PSBiYXJyaWVyKSB7IHRoaXNbYmFycmllclN5bV0gPSBudWxsOyB9XHJcbiAgICAgICAgICAgICAgICAgICAgYmFycmllci5yZXNvbHZlPy4oKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShvbnJlamVjdGVkIShyZWFzb24pKTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGVycik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgIH0sIGFzeW5jIChjYXVzZT8pID0+IHtcclxuICAgICAgICAgICAgLy9jYW5jZWxsZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIG9uY2FuY2VsbGVkPy4oY2F1c2UpO1xyXG4gICAgICAgICAgICB9IGZpbmFsbHkge1xyXG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5jYW5jZWwoY2F1c2UpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBBdHRhY2hlcyBhIGNhbGxiYWNrIGZvciBvbmx5IHRoZSByZWplY3Rpb24gb2YgdGhlIFByb21pc2UuXHJcbiAgICAgKlxyXG4gICAgICogVGhlIG9wdGlvbmFsIGBvbmNhbmNlbGxlZGAgYXJndW1lbnQgd2lsbCBiZSBpbnZva2VkIHdoZW4gdGhlIHJldHVybmVkIHByb21pc2UgaXMgY2FuY2VsbGVkLFxyXG4gICAgICogd2l0aCB0aGUgc2FtZSBzZW1hbnRpY3MgYXMgdGhlIGBvbmNhbmNlbGxlZGAgYXJndW1lbnQgb2YgdGhlIGNvbnN0cnVjdG9yLlxyXG4gICAgICogV2hlbiB0aGUgcGFyZW50IHByb21pc2UgcmVqZWN0cyBvciBpcyBjYW5jZWxsZWQsIHRoZSBgb25yZWplY3RlZGAgY2FsbGJhY2sgd2lsbCBydW4sXHJcbiAgICAgKiBfZXZlbiBhZnRlciB0aGUgcmV0dXJuZWQgcHJvbWlzZSBoYXMgYmVlbiBjYW5jZWxsZWQ6X1xyXG4gICAgICogaW4gdGhhdCBjYXNlLCBzaG91bGQgaXQgcmVqZWN0IG9yIHRocm93LCB0aGUgcmVhc29uIHdpbGwgYmUgd3JhcHBlZFxyXG4gICAgICogaW4gYSB7QGxpbmsgQ2FuY2VsbGVkUmVqZWN0aW9uRXJyb3J9IGFuZCBidWJibGVkIHVwIGFzIGFuIHVuaGFuZGxlZCByZWplY3Rpb24uXHJcbiAgICAgKlxyXG4gICAgICogSXQgaXMgZXF1aXZhbGVudCB0b1xyXG4gICAgICogYGBgdHNcclxuICAgICAqIGNhbmNlbGxhYmxlUHJvbWlzZS50aGVuKHVuZGVmaW5lZCwgb25yZWplY3RlZCwgb25jYW5jZWxsZWQpO1xyXG4gICAgICogYGBgXHJcbiAgICAgKiBhbmQgdGhlIHNhbWUgY2F2ZWF0cyBhcHBseS5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBBIFByb21pc2UgZm9yIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBjYWxsYmFjay5cclxuICAgICAqIENhbmNlbGxhdGlvbiByZXF1ZXN0cyBvbiB0aGUgcmV0dXJuZWQgcHJvbWlzZVxyXG4gICAgICogd2lsbCBwcm9wYWdhdGUgdXAgdGhlIGNoYWluIHRvIHRoZSBwYXJlbnQgcHJvbWlzZSxcclxuICAgICAqIGJ1dCBub3QgaW4gdGhlIG90aGVyIGRpcmVjdGlvbi5cclxuICAgICAqXHJcbiAgICAgKiBUaGUgcHJvbWlzZSByZXR1cm5lZCBmcm9tIHtAbGluayBjYW5jZWx9IHdpbGwgZnVsZmlsbCBvbmx5IGFmdGVyIGFsbCBhdHRhY2hlZCBoYW5kbGVyc1xyXG4gICAgICogdXAgdGhlIGVudGlyZSBwcm9taXNlIGNoYWluIGhhdmUgYmVlbiBydW4uXHJcbiAgICAgKlxyXG4gICAgICogSWYgYG9ucmVqZWN0ZWRgIHJldHVybnMgYSBjYW5jZWxsYWJsZSBwcm9taXNlLFxyXG4gICAgICogY2FuY2VsbGF0aW9uIHJlcXVlc3RzIHdpbGwgYmUgZGl2ZXJ0ZWQgdG8gaXQsXHJcbiAgICAgKiBhbmQgdGhlIHNwZWNpZmllZCBgb25jYW5jZWxsZWRgIGNhbGxiYWNrIHdpbGwgYmUgZGlzY2FyZGVkLlxyXG4gICAgICogU2VlIHtAbGluayB0aGVufSBmb3IgbW9yZSBkZXRhaWxzLlxyXG4gICAgICovXHJcbiAgICBjYXRjaDxUUmVzdWx0ID0gbmV2ZXI+KG9ucmVqZWN0ZWQ/OiAoKHJlYXNvbjogYW55KSA9PiAoUHJvbWlzZUxpa2U8VFJlc3VsdD4gfCBUUmVzdWx0KSkgfCB1bmRlZmluZWQgfCBudWxsLCBvbmNhbmNlbGxlZD86IENhbmNlbGxhYmxlUHJvbWlzZUNhbmNlbGxlcik6IENhbmNlbGxhYmxlUHJvbWlzZTxUIHwgVFJlc3VsdD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnRoZW4odW5kZWZpbmVkLCBvbnJlamVjdGVkLCBvbmNhbmNlbGxlZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBBdHRhY2hlcyBhIGNhbGxiYWNrIHRoYXQgaXMgaW52b2tlZCB3aGVuIHRoZSBDYW5jZWxsYWJsZVByb21pc2UgaXMgc2V0dGxlZCAoZnVsZmlsbGVkIG9yIHJlamVjdGVkKS4gVGhlXHJcbiAgICAgKiByZXNvbHZlZCB2YWx1ZSBjYW5ub3QgYmUgYWNjZXNzZWQgb3IgbW9kaWZpZWQgZnJvbSB0aGUgY2FsbGJhY2suXHJcbiAgICAgKiBUaGUgcmV0dXJuZWQgcHJvbWlzZSB3aWxsIHNldHRsZSBpbiB0aGUgc2FtZSBzdGF0ZSBhcyB0aGUgb3JpZ2luYWwgb25lXHJcbiAgICAgKiBhZnRlciB0aGUgcHJvdmlkZWQgY2FsbGJhY2sgaGFzIGNvbXBsZXRlZCBleGVjdXRpb24sXHJcbiAgICAgKiB1bmxlc3MgdGhlIGNhbGxiYWNrIHRocm93cyBvciByZXR1cm5zIGEgcmVqZWN0aW5nIHByb21pc2UsXHJcbiAgICAgKiBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCBwcm9taXNlIHdpbGwgcmVqZWN0IGFzIHdlbGwuXHJcbiAgICAgKlxyXG4gICAgICogVGhlIG9wdGlvbmFsIGBvbmNhbmNlbGxlZGAgYXJndW1lbnQgd2lsbCBiZSBpbnZva2VkIHdoZW4gdGhlIHJldHVybmVkIHByb21pc2UgaXMgY2FuY2VsbGVkLFxyXG4gICAgICogd2l0aCB0aGUgc2FtZSBzZW1hbnRpY3MgYXMgdGhlIGBvbmNhbmNlbGxlZGAgYXJndW1lbnQgb2YgdGhlIGNvbnN0cnVjdG9yLlxyXG4gICAgICogT25jZSB0aGUgcGFyZW50IHByb21pc2Ugc2V0dGxlcywgdGhlIGBvbmZpbmFsbHlgIGNhbGxiYWNrIHdpbGwgcnVuLFxyXG4gICAgICogX2V2ZW4gYWZ0ZXIgdGhlIHJldHVybmVkIHByb21pc2UgaGFzIGJlZW4gY2FuY2VsbGVkOl9cclxuICAgICAqIGluIHRoYXQgY2FzZSwgc2hvdWxkIGl0IHJlamVjdCBvciB0aHJvdywgdGhlIHJlYXNvbiB3aWxsIGJlIHdyYXBwZWRcclxuICAgICAqIGluIGEge0BsaW5rIENhbmNlbGxlZFJlamVjdGlvbkVycm9yfSBhbmQgYnViYmxlZCB1cCBhcyBhbiB1bmhhbmRsZWQgcmVqZWN0aW9uLlxyXG4gICAgICpcclxuICAgICAqIFRoaXMgbWV0aG9kIGlzIGltcGxlbWVudGVkIGluIHRlcm1zIG9mIHtAbGluayB0aGVufSBhbmQgdGhlIHNhbWUgY2F2ZWF0cyBhcHBseS5cclxuICAgICAqIEl0IGlzIHBvbHlmaWxsZWQsIGhlbmNlIGF2YWlsYWJsZSBpbiBldmVyeSBPUy93ZWJ2aWV3IHZlcnNpb24uXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgQSBQcm9taXNlIGZvciB0aGUgY29tcGxldGlvbiBvZiB0aGUgY2FsbGJhY2suXHJcbiAgICAgKiBDYW5jZWxsYXRpb24gcmVxdWVzdHMgb24gdGhlIHJldHVybmVkIHByb21pc2VcclxuICAgICAqIHdpbGwgcHJvcGFnYXRlIHVwIHRoZSBjaGFpbiB0byB0aGUgcGFyZW50IHByb21pc2UsXHJcbiAgICAgKiBidXQgbm90IGluIHRoZSBvdGhlciBkaXJlY3Rpb24uXHJcbiAgICAgKlxyXG4gICAgICogVGhlIHByb21pc2UgcmV0dXJuZWQgZnJvbSB7QGxpbmsgY2FuY2VsfSB3aWxsIGZ1bGZpbGwgb25seSBhZnRlciBhbGwgYXR0YWNoZWQgaGFuZGxlcnNcclxuICAgICAqIHVwIHRoZSBlbnRpcmUgcHJvbWlzZSBjaGFpbiBoYXZlIGJlZW4gcnVuLlxyXG4gICAgICpcclxuICAgICAqIElmIGBvbmZpbmFsbHlgIHJldHVybnMgYSBjYW5jZWxsYWJsZSBwcm9taXNlLFxyXG4gICAgICogY2FuY2VsbGF0aW9uIHJlcXVlc3RzIHdpbGwgYmUgZGl2ZXJ0ZWQgdG8gaXQsXHJcbiAgICAgKiBhbmQgdGhlIHNwZWNpZmllZCBgb25jYW5jZWxsZWRgIGNhbGxiYWNrIHdpbGwgYmUgZGlzY2FyZGVkLlxyXG4gICAgICogU2VlIHtAbGluayB0aGVufSBmb3IgbW9yZSBkZXRhaWxzLlxyXG4gICAgICovXHJcbiAgICBmaW5hbGx5KG9uZmluYWxseT86ICgoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZCB8IG51bGwsIG9uY2FuY2VsbGVkPzogQ2FuY2VsbGFibGVQcm9taXNlQ2FuY2VsbGVyKTogQ2FuY2VsbGFibGVQcm9taXNlPFQ+IHtcclxuICAgICAgICBpZiAoISh0aGlzIGluc3RhbmNlb2YgQ2FuY2VsbGFibGVQcm9taXNlKSkge1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2FuY2VsbGFibGVQcm9taXNlLnByb3RvdHlwZS5maW5hbGx5IGNhbGxlZCBvbiBhbiBpbnZhbGlkIG9iamVjdC5cIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoIWlzQ2FsbGFibGUob25maW5hbGx5KSkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy50aGVuKG9uZmluYWxseSwgb25maW5hbGx5LCBvbmNhbmNlbGxlZCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy50aGVuKFxyXG4gICAgICAgICAgICAodmFsdWUpID0+IENhbmNlbGxhYmxlUHJvbWlzZS5yZXNvbHZlKG9uZmluYWxseSgpKS50aGVuKCgpID0+IHZhbHVlKSxcclxuICAgICAgICAgICAgKHJlYXNvbj8pID0+IENhbmNlbGxhYmxlUHJvbWlzZS5yZXNvbHZlKG9uZmluYWxseSgpKS50aGVuKCgpID0+IHsgdGhyb3cgcmVhc29uOyB9KSxcclxuICAgICAgICAgICAgb25jYW5jZWxsZWQsXHJcbiAgICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFdlIHVzZSB0aGUgYFtTeW1ib2wuc3BlY2llc11gIHN0YXRpYyBwcm9wZXJ0eSwgaWYgYXZhaWxhYmxlLFxyXG4gICAgICogdG8gZGlzYWJsZSB0aGUgYnVpbHQtaW4gYXV0b21hdGljIHN1YmNsYXNzaW5nIGZlYXR1cmVzIGZyb20ge0BsaW5rIFByb21pc2V9LlxyXG4gICAgICogSXQgaXMgY3JpdGljYWwgZm9yIHBlcmZvcm1hbmNlIHJlYXNvbnMgdGhhdCBleHRlbmRlcnMgZG8gbm90IG92ZXJyaWRlIHRoaXMuXHJcbiAgICAgKiBPbmNlIHRoZSBwcm9wb3NhbCBhdCBodHRwczovL2dpdGh1Yi5jb20vdGMzOS9wcm9wb3NhbC1ybS1idWlsdGluLXN1YmNsYXNzaW5nXHJcbiAgICAgKiBpcyBlaXRoZXIgYWNjZXB0ZWQgb3IgcmV0aXJlZCwgdGhpcyBpbXBsZW1lbnRhdGlvbiB3aWxsIGhhdmUgdG8gYmUgcmV2aXNlZCBhY2NvcmRpbmdseS5cclxuICAgICAqXHJcbiAgICAgKiBAaWdub3JlXHJcbiAgICAgKiBAaW50ZXJuYWxcclxuICAgICAqL1xyXG4gICAgc3RhdGljIGdldCBbc3BlY2llc10oKSB7XHJcbiAgICAgICAgcmV0dXJuIFByb21pc2U7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgQ2FuY2VsbGFibGVQcm9taXNlIHRoYXQgaXMgcmVzb2x2ZWQgd2l0aCBhbiBhcnJheSBvZiByZXN1bHRzXHJcbiAgICAgKiB3aGVuIGFsbCBvZiB0aGUgcHJvdmlkZWQgUHJvbWlzZXMgcmVzb2x2ZSwgb3IgcmVqZWN0ZWQgd2hlbiBhbnkgUHJvbWlzZSBpcyByZWplY3RlZC5cclxuICAgICAqXHJcbiAgICAgKiBFdmVyeSBvbmUgb2YgdGhlIHByb3ZpZGVkIG9iamVjdHMgdGhhdCBpcyBhIHRoZW5hYmxlIF9hbmRfIGNhbmNlbGxhYmxlIG9iamVjdFxyXG4gICAgICogd2lsbCBiZSBjYW5jZWxsZWQgd2hlbiB0aGUgcmV0dXJuZWQgcHJvbWlzZSBpcyBjYW5jZWxsZWQsIHdpdGggdGhlIHNhbWUgY2F1c2UuXHJcbiAgICAgKlxyXG4gICAgICogQGdyb3VwIFN0YXRpYyBNZXRob2RzXHJcbiAgICAgKi9cclxuICAgIHN0YXRpYyBhbGw8VD4odmFsdWVzOiBJdGVyYWJsZTxUIHwgUHJvbWlzZUxpa2U8VD4+KTogQ2FuY2VsbGFibGVQcm9taXNlPEF3YWl0ZWQ8VD5bXT47XHJcbiAgICBzdGF0aWMgYWxsPFQgZXh0ZW5kcyByZWFkb25seSB1bmtub3duW10gfCBbXT4odmFsdWVzOiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPHsgLXJlYWRvbmx5IFtQIGluIGtleW9mIFRdOiBBd2FpdGVkPFRbUF0+OyB9PjtcclxuICAgIHN0YXRpYyBhbGw8VCBleHRlbmRzIEl0ZXJhYmxlPHVua25vd24+IHwgQXJyYXlMaWtlPHVua25vd24+Pih2YWx1ZXM6IFQpOiBDYW5jZWxsYWJsZVByb21pc2U8dW5rbm93bj4ge1xyXG4gICAgICAgIGxldCBjb2xsZWN0ZWQgPSBBcnJheS5mcm9tKHZhbHVlcyk7XHJcbiAgICAgICAgY29uc3QgcHJvbWlzZSA9IGNvbGxlY3RlZC5sZW5ndGggPT09IDBcclxuICAgICAgICAgICAgPyBDYW5jZWxsYWJsZVByb21pc2UucmVzb2x2ZShjb2xsZWN0ZWQpXHJcbiAgICAgICAgICAgIDogbmV3IENhbmNlbGxhYmxlUHJvbWlzZTx1bmtub3duPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICB2b2lkIFByb21pc2UuYWxsKGNvbGxlY3RlZCkudGhlbihyZXNvbHZlLCByZWplY3QpO1xyXG4gICAgICAgICAgICB9LCAoY2F1c2U/KTogUHJvbWlzZTx2b2lkPiA9PiBjYW5jZWxBbGwocHJvbWlzZSwgY29sbGVjdGVkLCBjYXVzZSkpO1xyXG4gICAgICAgIHJldHVybiBwcm9taXNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIENhbmNlbGxhYmxlUHJvbWlzZSB0aGF0IGlzIHJlc29sdmVkIHdpdGggYW4gYXJyYXkgb2YgcmVzdWx0c1xyXG4gICAgICogd2hlbiBhbGwgb2YgdGhlIHByb3ZpZGVkIFByb21pc2VzIHJlc29sdmUgb3IgcmVqZWN0LlxyXG4gICAgICpcclxuICAgICAqIEV2ZXJ5IG9uZSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0cyB0aGF0IGlzIGEgdGhlbmFibGUgX2FuZF8gY2FuY2VsbGFibGUgb2JqZWN0XHJcbiAgICAgKiB3aWxsIGJlIGNhbmNlbGxlZCB3aGVuIHRoZSByZXR1cm5lZCBwcm9taXNlIGlzIGNhbmNlbGxlZCwgd2l0aCB0aGUgc2FtZSBjYXVzZS5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIGFsbFNldHRsZWQ8VD4odmFsdWVzOiBJdGVyYWJsZTxUIHwgUHJvbWlzZUxpa2U8VD4+KTogQ2FuY2VsbGFibGVQcm9taXNlPFByb21pc2VTZXR0bGVkUmVzdWx0PEF3YWl0ZWQ8VD4+W10+O1xyXG4gICAgc3RhdGljIGFsbFNldHRsZWQ8VCBleHRlbmRzIHJlYWRvbmx5IHVua25vd25bXSB8IFtdPih2YWx1ZXM6IFQpOiBDYW5jZWxsYWJsZVByb21pc2U8eyAtcmVhZG9ubHkgW1AgaW4ga2V5b2YgVF06IFByb21pc2VTZXR0bGVkUmVzdWx0PEF3YWl0ZWQ8VFtQXT4+OyB9PjtcclxuICAgIHN0YXRpYyBhbGxTZXR0bGVkPFQgZXh0ZW5kcyBJdGVyYWJsZTx1bmtub3duPiB8IEFycmF5TGlrZTx1bmtub3duPj4odmFsdWVzOiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPHVua25vd24+IHtcclxuICAgICAgICBsZXQgY29sbGVjdGVkID0gQXJyYXkuZnJvbSh2YWx1ZXMpO1xyXG4gICAgICAgIGNvbnN0IHByb21pc2UgPSBjb2xsZWN0ZWQubGVuZ3RoID09PSAwXHJcbiAgICAgICAgICAgID8gQ2FuY2VsbGFibGVQcm9taXNlLnJlc29sdmUoY29sbGVjdGVkKVxyXG4gICAgICAgICAgICA6IG5ldyBDYW5jZWxsYWJsZVByb21pc2U8dW5rbm93bj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgdm9pZCBQcm9taXNlLmFsbFNldHRsZWQoY29sbGVjdGVkKS50aGVuKHJlc29sdmUsIHJlamVjdCk7XHJcbiAgICAgICAgICAgIH0sIChjYXVzZT8pOiBQcm9taXNlPHZvaWQ+ID0+IGNhbmNlbEFsbChwcm9taXNlLCBjb2xsZWN0ZWQsIGNhdXNlKSk7XHJcbiAgICAgICAgcmV0dXJuIHByb21pc2U7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBUaGUgYW55IGZ1bmN0aW9uIHJldHVybnMgYSBwcm9taXNlIHRoYXQgaXMgZnVsZmlsbGVkIGJ5IHRoZSBmaXJzdCBnaXZlbiBwcm9taXNlIHRvIGJlIGZ1bGZpbGxlZCxcclxuICAgICAqIG9yIHJlamVjdGVkIHdpdGggYW4gQWdncmVnYXRlRXJyb3IgY29udGFpbmluZyBhbiBhcnJheSBvZiByZWplY3Rpb24gcmVhc29uc1xyXG4gICAgICogaWYgYWxsIG9mIHRoZSBnaXZlbiBwcm9taXNlcyBhcmUgcmVqZWN0ZWQuXHJcbiAgICAgKiBJdCByZXNvbHZlcyBhbGwgZWxlbWVudHMgb2YgdGhlIHBhc3NlZCBpdGVyYWJsZSB0byBwcm9taXNlcyBhcyBpdCBydW5zIHRoaXMgYWxnb3JpdGhtLlxyXG4gICAgICpcclxuICAgICAqIEV2ZXJ5IG9uZSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0cyB0aGF0IGlzIGEgdGhlbmFibGUgX2FuZF8gY2FuY2VsbGFibGUgb2JqZWN0XHJcbiAgICAgKiB3aWxsIGJlIGNhbmNlbGxlZCB3aGVuIHRoZSByZXR1cm5lZCBwcm9taXNlIGlzIGNhbmNlbGxlZCwgd2l0aCB0aGUgc2FtZSBjYXVzZS5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIGFueTxUPih2YWx1ZXM6IEl0ZXJhYmxlPFQgfCBQcm9taXNlTGlrZTxUPj4pOiBDYW5jZWxsYWJsZVByb21pc2U8QXdhaXRlZDxUPj47XHJcbiAgICBzdGF0aWMgYW55PFQgZXh0ZW5kcyByZWFkb25seSB1bmtub3duW10gfCBbXT4odmFsdWVzOiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPEF3YWl0ZWQ8VFtudW1iZXJdPj47XHJcbiAgICBzdGF0aWMgYW55PFQgZXh0ZW5kcyBJdGVyYWJsZTx1bmtub3duPiB8IEFycmF5TGlrZTx1bmtub3duPj4odmFsdWVzOiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPHVua25vd24+IHtcclxuICAgICAgICBsZXQgY29sbGVjdGVkID0gQXJyYXkuZnJvbSh2YWx1ZXMpO1xyXG4gICAgICAgIGNvbnN0IHByb21pc2UgPSBjb2xsZWN0ZWQubGVuZ3RoID09PSAwXHJcbiAgICAgICAgICAgID8gQ2FuY2VsbGFibGVQcm9taXNlLnJlc29sdmUoY29sbGVjdGVkKVxyXG4gICAgICAgICAgICA6IG5ldyBDYW5jZWxsYWJsZVByb21pc2U8dW5rbm93bj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgdm9pZCBQcm9taXNlLmFueShjb2xsZWN0ZWQpLnRoZW4ocmVzb2x2ZSwgcmVqZWN0KTtcclxuICAgICAgICAgICAgfSwgKGNhdXNlPyk6IFByb21pc2U8dm9pZD4gPT4gY2FuY2VsQWxsKHByb21pc2UsIGNvbGxlY3RlZCwgY2F1c2UpKTtcclxuICAgICAgICByZXR1cm4gcHJvbWlzZTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBQcm9taXNlIHRoYXQgaXMgcmVzb2x2ZWQgb3IgcmVqZWN0ZWQgd2hlbiBhbnkgb2YgdGhlIHByb3ZpZGVkIFByb21pc2VzIGFyZSByZXNvbHZlZCBvciByZWplY3RlZC5cclxuICAgICAqXHJcbiAgICAgKiBFdmVyeSBvbmUgb2YgdGhlIHByb3ZpZGVkIG9iamVjdHMgdGhhdCBpcyBhIHRoZW5hYmxlIF9hbmRfIGNhbmNlbGxhYmxlIG9iamVjdFxyXG4gICAgICogd2lsbCBiZSBjYW5jZWxsZWQgd2hlbiB0aGUgcmV0dXJuZWQgcHJvbWlzZSBpcyBjYW5jZWxsZWQsIHdpdGggdGhlIHNhbWUgY2F1c2UuXHJcbiAgICAgKlxyXG4gICAgICogQGdyb3VwIFN0YXRpYyBNZXRob2RzXHJcbiAgICAgKi9cclxuICAgIHN0YXRpYyByYWNlPFQ+KHZhbHVlczogSXRlcmFibGU8VCB8IFByb21pc2VMaWtlPFQ+Pik6IENhbmNlbGxhYmxlUHJvbWlzZTxBd2FpdGVkPFQ+PjtcclxuICAgIHN0YXRpYyByYWNlPFQgZXh0ZW5kcyByZWFkb25seSB1bmtub3duW10gfCBbXT4odmFsdWVzOiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPEF3YWl0ZWQ8VFtudW1iZXJdPj47XHJcbiAgICBzdGF0aWMgcmFjZTxUIGV4dGVuZHMgSXRlcmFibGU8dW5rbm93bj4gfCBBcnJheUxpa2U8dW5rbm93bj4+KHZhbHVlczogVCk6IENhbmNlbGxhYmxlUHJvbWlzZTx1bmtub3duPiB7XHJcbiAgICAgICAgbGV0IGNvbGxlY3RlZCA9IEFycmF5LmZyb20odmFsdWVzKTtcclxuICAgICAgICBjb25zdCBwcm9taXNlID0gbmV3IENhbmNlbGxhYmxlUHJvbWlzZTx1bmtub3duPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgIHZvaWQgUHJvbWlzZS5yYWNlKGNvbGxlY3RlZCkudGhlbihyZXNvbHZlLCByZWplY3QpO1xyXG4gICAgICAgIH0sIChjYXVzZT8pOiBQcm9taXNlPHZvaWQ+ID0+IGNhbmNlbEFsbChwcm9taXNlLCBjb2xsZWN0ZWQsIGNhdXNlKSk7XHJcbiAgICAgICAgcmV0dXJuIHByb21pc2U7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgbmV3IGNhbmNlbGxlZCBDYW5jZWxsYWJsZVByb21pc2UgZm9yIHRoZSBwcm92aWRlZCBjYXVzZS5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIGNhbmNlbDxUID0gbmV2ZXI+KGNhdXNlPzogYW55KTogQ2FuY2VsbGFibGVQcm9taXNlPFQ+IHtcclxuICAgICAgICBjb25zdCBwID0gbmV3IENhbmNlbGxhYmxlUHJvbWlzZTxUPigoKSA9PiB7fSk7XHJcbiAgICAgICAgcC5jYW5jZWwoY2F1c2UpO1xyXG4gICAgICAgIHJldHVybiBwO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyBDYW5jZWxsYWJsZVByb21pc2UgdGhhdCBjYW5jZWxzXHJcbiAgICAgKiBhZnRlciB0aGUgc3BlY2lmaWVkIHRpbWVvdXQsIHdpdGggdGhlIHByb3ZpZGVkIGNhdXNlLlxyXG4gICAgICpcclxuICAgICAqIElmIHRoZSB7QGxpbmsgQWJvcnRTaWduYWwudGltZW91dH0gZmFjdG9yeSBtZXRob2QgaXMgYXZhaWxhYmxlLFxyXG4gICAgICogaXQgaXMgdXNlZCB0byBiYXNlIHRoZSB0aW1lb3V0IG9uIF9hY3RpdmVfIHRpbWUgcmF0aGVyIHRoYW4gX2VsYXBzZWRfIHRpbWUuXHJcbiAgICAgKiBPdGhlcndpc2UsIGB0aW1lb3V0YCBmYWxscyBiYWNrIHRvIHtAbGluayBzZXRUaW1lb3V0fS5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIHRpbWVvdXQ8VCA9IG5ldmVyPihtaWxsaXNlY29uZHM6IG51bWJlciwgY2F1c2U/OiBhbnkpOiBDYW5jZWxsYWJsZVByb21pc2U8VD4ge1xyXG4gICAgICAgIGNvbnN0IHByb21pc2UgPSBuZXcgQ2FuY2VsbGFibGVQcm9taXNlPFQ+KCgpID0+IHt9KTtcclxuICAgICAgICBpZiAoQWJvcnRTaWduYWwgJiYgdHlwZW9mIEFib3J0U2lnbmFsID09PSAnZnVuY3Rpb24nICYmIEFib3J0U2lnbmFsLnRpbWVvdXQgJiYgdHlwZW9mIEFib3J0U2lnbmFsLnRpbWVvdXQgPT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgICAgICAgQWJvcnRTaWduYWwudGltZW91dChtaWxsaXNlY29uZHMpLmFkZEV2ZW50TGlzdGVuZXIoJ2Fib3J0JywgKCkgPT4gdm9pZCBwcm9taXNlLmNhbmNlbChjYXVzZSkpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdm9pZCBwcm9taXNlLmNhbmNlbChjYXVzZSksIG1pbGxpc2Vjb25kcyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBwcm9taXNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyBDYW5jZWxsYWJsZVByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgc3BlY2lmaWVkIHRpbWVvdXQuXHJcbiAgICAgKiBUaGUgcmV0dXJuZWQgcHJvbWlzZSBjYW4gYmUgY2FuY2VsbGVkIHdpdGhvdXQgY29uc2VxdWVuY2VzLlxyXG4gICAgICpcclxuICAgICAqIEBncm91cCBTdGF0aWMgTWV0aG9kc1xyXG4gICAgICovXHJcbiAgICBzdGF0aWMgc2xlZXAobWlsbGlzZWNvbmRzOiBudW1iZXIpOiBDYW5jZWxsYWJsZVByb21pc2U8dm9pZD47XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgQ2FuY2VsbGFibGVQcm9taXNlIHRoYXQgcmVzb2x2ZXMgYWZ0ZXJcclxuICAgICAqIHRoZSBzcGVjaWZpZWQgdGltZW91dCwgd2l0aCB0aGUgcHJvdmlkZWQgdmFsdWUuXHJcbiAgICAgKiBUaGUgcmV0dXJuZWQgcHJvbWlzZSBjYW4gYmUgY2FuY2VsbGVkIHdpdGhvdXQgY29uc2VxdWVuY2VzLlxyXG4gICAgICpcclxuICAgICAqIEBncm91cCBTdGF0aWMgTWV0aG9kc1xyXG4gICAgICovXHJcbiAgICBzdGF0aWMgc2xlZXA8VD4obWlsbGlzZWNvbmRzOiBudW1iZXIsIHZhbHVlOiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPFQ+O1xyXG4gICAgc3RhdGljIHNsZWVwPFQgPSB2b2lkPihtaWxsaXNlY29uZHM6IG51bWJlciwgdmFsdWU/OiBUKTogQ2FuY2VsbGFibGVQcm9taXNlPFQ+IHtcclxuICAgICAgICByZXR1cm4gbmV3IENhbmNlbGxhYmxlUHJvbWlzZTxUPigocmVzb2x2ZSkgPT4ge1xyXG4gICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHJlc29sdmUodmFsdWUhKSwgbWlsbGlzZWNvbmRzKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgcmVqZWN0ZWQgQ2FuY2VsbGFibGVQcm9taXNlIGZvciB0aGUgcHJvdmlkZWQgcmVhc29uLlxyXG4gICAgICpcclxuICAgICAqIEBncm91cCBTdGF0aWMgTWV0aG9kc1xyXG4gICAgICovXHJcbiAgICBzdGF0aWMgcmVqZWN0PFQgPSBuZXZlcj4ocmVhc29uPzogYW55KTogQ2FuY2VsbGFibGVQcm9taXNlPFQ+IHtcclxuICAgICAgICByZXR1cm4gbmV3IENhbmNlbGxhYmxlUHJvbWlzZTxUPigoXywgcmVqZWN0KSA9PiByZWplY3QocmVhc29uKSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHJlc29sdmVkIENhbmNlbGxhYmxlUHJvbWlzZS5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIHJlc29sdmUoKTogQ2FuY2VsbGFibGVQcm9taXNlPHZvaWQ+O1xyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHJlc29sdmVkIENhbmNlbGxhYmxlUHJvbWlzZSBmb3IgdGhlIHByb3ZpZGVkIHZhbHVlLlxyXG4gICAgICpcclxuICAgICAqIEBncm91cCBTdGF0aWMgTWV0aG9kc1xyXG4gICAgICovXHJcbiAgICBzdGF0aWMgcmVzb2x2ZTxUPih2YWx1ZTogVCk6IENhbmNlbGxhYmxlUHJvbWlzZTxBd2FpdGVkPFQ+PjtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyByZXNvbHZlZCBDYW5jZWxsYWJsZVByb21pc2UgZm9yIHRoZSBwcm92aWRlZCB2YWx1ZS5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIHJlc29sdmU8VD4odmFsdWU6IFQgfCBQcm9taXNlTGlrZTxUPik6IENhbmNlbGxhYmxlUHJvbWlzZTxBd2FpdGVkPFQ+PjtcclxuICAgIHN0YXRpYyByZXNvbHZlPFQgPSB2b2lkPih2YWx1ZT86IFQgfCBQcm9taXNlTGlrZTxUPik6IENhbmNlbGxhYmxlUHJvbWlzZTxBd2FpdGVkPFQ+PiB7XHJcbiAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgQ2FuY2VsbGFibGVQcm9taXNlKSB7XHJcbiAgICAgICAgICAgIC8vIE9wdGltaXNlIGZvciBjYW5jZWxsYWJsZSBwcm9taXNlcy5cclxuICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbmV3IENhbmNlbGxhYmxlUHJvbWlzZTxhbnk+KChyZXNvbHZlKSA9PiByZXNvbHZlKHZhbHVlKSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgbmV3IENhbmNlbGxhYmxlUHJvbWlzZSBhbmQgcmV0dXJucyBpdCBpbiBhbiBvYmplY3QsIGFsb25nIHdpdGggaXRzIHJlc29sdmUgYW5kIHJlamVjdCBmdW5jdGlvbnNcclxuICAgICAqIGFuZCBhIGdldHRlci9zZXR0ZXIgZm9yIHRoZSBjYW5jZWxsYXRpb24gY2FsbGJhY2suXHJcbiAgICAgKlxyXG4gICAgICogVGhpcyBtZXRob2QgaXMgcG9seWZpbGxlZCwgaGVuY2UgYXZhaWxhYmxlIGluIGV2ZXJ5IE9TL3dlYnZpZXcgdmVyc2lvbi5cclxuICAgICAqXHJcbiAgICAgKiBAZ3JvdXAgU3RhdGljIE1ldGhvZHNcclxuICAgICAqL1xyXG4gICAgc3RhdGljIHdpdGhSZXNvbHZlcnM8VD4oKTogQ2FuY2VsbGFibGVQcm9taXNlV2l0aFJlc29sdmVyczxUPiB7XHJcbiAgICAgICAgbGV0IHJlc3VsdDogQ2FuY2VsbGFibGVQcm9taXNlV2l0aFJlc29sdmVyczxUPiA9IHsgb25jYW5jZWxsZWQ6IG51bGwgfSBhcyBhbnk7XHJcbiAgICAgICAgcmVzdWx0LnByb21pc2UgPSBuZXcgQ2FuY2VsbGFibGVQcm9taXNlPFQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgcmVzdWx0LnJlc29sdmUgPSByZXNvbHZlO1xyXG4gICAgICAgICAgICByZXN1bHQucmVqZWN0ID0gcmVqZWN0O1xyXG4gICAgICAgIH0sIChjYXVzZT86IGFueSkgPT4geyByZXN1bHQub25jYW5jZWxsZWQ/LihjYXVzZSk7IH0pO1xyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIGEgY2FsbGJhY2sgdGhhdCBpbXBsZW1lbnRzIHRoZSBjYW5jZWxsYXRpb24gYWxnb3JpdGhtIGZvciB0aGUgZ2l2ZW4gY2FuY2VsbGFibGUgcHJvbWlzZS5cclxuICogVGhlIHByb21pc2UgcmV0dXJuZWQgZnJvbSB0aGUgcmVzdWx0aW5nIGZ1bmN0aW9uIGRvZXMgbm90IHJlamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIGNhbmNlbGxlckZvcjxUPihwcm9taXNlOiBDYW5jZWxsYWJsZVByb21pc2VXaXRoUmVzb2x2ZXJzPFQ+LCBzdGF0ZTogQ2FuY2VsbGFibGVQcm9taXNlU3RhdGUpIHtcclxuICAgIGxldCBjYW5jZWxsYXRpb25Qcm9taXNlOiB2b2lkIHwgUHJvbWlzZUxpa2U8dm9pZD4gPSB1bmRlZmluZWQ7XHJcblxyXG4gICAgcmV0dXJuIChyZWFzb246IENhbmNlbEVycm9yKTogdm9pZCB8IFByb21pc2VMaWtlPHZvaWQ+ID0+IHtcclxuICAgICAgICBpZiAoIXN0YXRlLnNldHRsZWQpIHtcclxuICAgICAgICAgICAgc3RhdGUuc2V0dGxlZCA9IHRydWU7XHJcbiAgICAgICAgICAgIHN0YXRlLnJlYXNvbiA9IHJlYXNvbjtcclxuICAgICAgICAgICAgcHJvbWlzZS5yZWplY3QocmVhc29uKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEF0dGFjaCBhbiBlcnJvciBoYW5kbGVyIHRoYXQgaWdub3JlcyB0aGlzIHNwZWNpZmljIHJlamVjdGlvbiByZWFzb24gYW5kIG5vdGhpbmcgZWxzZS5cclxuICAgICAgICAgICAgLy8gSW4gdGhlb3J5LCBhIHNhbmUgdW5kZXJseWluZyBpbXBsZW1lbnRhdGlvbiBhdCB0aGlzIHBvaW50XHJcbiAgICAgICAgICAgIC8vIHNob3VsZCBhbHdheXMgcmVqZWN0IHdpdGggb3VyIGNhbmNlbGxhdGlvbiByZWFzb24sXHJcbiAgICAgICAgICAgIC8vIGhlbmNlIHRoZSBoYW5kbGVyIHdpbGwgbmV2ZXIgdGhyb3cuXHJcbiAgICAgICAgICAgIHZvaWQgUHJvbWlzZS5wcm90b3R5cGUudGhlbi5jYWxsKHByb21pc2UucHJvbWlzZSwgdW5kZWZpbmVkLCAoZXJyKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoZXJyICE9PSByZWFzb24pIHtcclxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBlcnI7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgcmVhc29uIGlzIG5vdCBzZXQsIHRoZSBwcm9taXNlIHJlc29sdmVkIHJlZ3VsYXJseSwgaGVuY2Ugd2UgbXVzdCBub3QgY2FsbCBvbmNhbmNlbGxlZC5cclxuICAgICAgICAvLyBJZiBvbmNhbmNlbGxlZCBpcyB1bnNldCwgbm8gbmVlZCB0byBnbyBhbnkgZnVydGhlci5cclxuICAgICAgICBpZiAoIXN0YXRlLnJlYXNvbiB8fCAhcHJvbWlzZS5vbmNhbmNlbGxlZCkgeyByZXR1cm47IH1cclxuXHJcbiAgICAgICAgY2FuY2VsbGF0aW9uUHJvbWlzZSA9IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICByZXNvbHZlKHByb21pc2Uub25jYW5jZWxsZWQhKHN0YXRlLnJlYXNvbiEuY2F1c2UpKTtcclxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XHJcbiAgICAgICAgICAgICAgICBQcm9taXNlLnJlamVjdChuZXcgQ2FuY2VsbGVkUmVqZWN0aW9uRXJyb3IocHJvbWlzZS5wcm9taXNlLCBlcnIsIFwiVW5oYW5kbGVkIGV4Y2VwdGlvbiBpbiBvbmNhbmNlbGxlZCBjYWxsYmFjay5cIikpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfSkuY2F0Y2goKHJlYXNvbj8pID0+IHtcclxuICAgICAgICAgICAgUHJvbWlzZS5yZWplY3QobmV3IENhbmNlbGxlZFJlamVjdGlvbkVycm9yKHByb21pc2UucHJvbWlzZSwgcmVhc29uLCBcIlVuaGFuZGxlZCByZWplY3Rpb24gaW4gb25jYW5jZWxsZWQgY2FsbGJhY2suXCIpKTtcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVW5zZXQgb25jYW5jZWxsZWQgdG8gcHJldmVudCByZXBlYXRlZCBjYWxscy5cclxuICAgICAgICBwcm9taXNlLm9uY2FuY2VsbGVkID0gbnVsbDtcclxuXHJcbiAgICAgICAgcmV0dXJuIGNhbmNlbGxhdGlvblByb21pc2U7XHJcbiAgICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIGEgY2FsbGJhY2sgdGhhdCBpbXBsZW1lbnRzIHRoZSByZXNvbHV0aW9uIGFsZ29yaXRobSBmb3IgdGhlIGdpdmVuIGNhbmNlbGxhYmxlIHByb21pc2UuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXNvbHZlckZvcjxUPihwcm9taXNlOiBDYW5jZWxsYWJsZVByb21pc2VXaXRoUmVzb2x2ZXJzPFQ+LCBzdGF0ZTogQ2FuY2VsbGFibGVQcm9taXNlU3RhdGUpOiBDYW5jZWxsYWJsZVByb21pc2VSZXNvbHZlcjxUPiB7XHJcbiAgICByZXR1cm4gKHZhbHVlKSA9PiB7XHJcbiAgICAgICAgaWYgKHN0YXRlLnJlc29sdmluZykgeyByZXR1cm47IH1cclxuICAgICAgICBzdGF0ZS5yZXNvbHZpbmcgPSB0cnVlO1xyXG5cclxuICAgICAgICBpZiAodmFsdWUgPT09IHByb21pc2UucHJvbWlzZSkge1xyXG4gICAgICAgICAgICBpZiAoc3RhdGUuc2V0dGxlZCkgeyByZXR1cm47IH1cclxuICAgICAgICAgICAgc3RhdGUuc2V0dGxlZCA9IHRydWU7XHJcbiAgICAgICAgICAgIHByb21pc2UucmVqZWN0KG5ldyBUeXBlRXJyb3IoXCJBIHByb21pc2UgY2Fubm90IGJlIHJlc29sdmVkIHdpdGggaXRzZWxmLlwiKSk7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICh2YWx1ZSAhPSBudWxsICYmICh0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnIHx8IHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJykpIHtcclxuICAgICAgICAgICAgbGV0IHRoZW46IGFueTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIHRoZW4gPSAodmFsdWUgYXMgYW55KS50aGVuO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgICAgICAgICAgICAgIHN0YXRlLnNldHRsZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgcHJvbWlzZS5yZWplY3QoZXJyKTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaWYgKGlzQ2FsbGFibGUodGhlbikpIHtcclxuICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgbGV0IGNhbmNlbCA9ICh2YWx1ZSBhcyBhbnkpLmNhbmNlbDtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoaXNDYWxsYWJsZShjYW5jZWwpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG9uY2FuY2VsbGVkID0gKGNhdXNlPzogYW55KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWZsZWN0LmFwcGx5KGNhbmNlbCwgdmFsdWUsIFtjYXVzZV0pO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc3RhdGUucmVhc29uKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBhbHJlYWR5IGNhbmNlbGxlZCwgcHJvcGFnYXRlIGNhbmNlbGxhdGlvbi5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRoZSBwcm9taXNlIHJldHVybmVkIGZyb20gdGhlIGNhbmNlbGxlciBhbGdvcml0aG0gZG9lcyBub3QgcmVqZWN0XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBzbyBpdCBjYW4gYmUgZGlzY2FyZGVkIHNhZmVseS5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZvaWQgY2FuY2VsbGVyRm9yKHsgLi4ucHJvbWlzZSwgb25jYW5jZWxsZWQgfSwgc3RhdGUpKHN0YXRlLnJlYXNvbik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9taXNlLm9uY2FuY2VsbGVkID0gb25jYW5jZWxsZWQ7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgICAgICAgICAgICAgY29uc3QgbmV3U3RhdGU6IENhbmNlbGxhYmxlUHJvbWlzZVN0YXRlID0ge1xyXG4gICAgICAgICAgICAgICAgICAgIHJvb3Q6IHN0YXRlLnJvb3QsXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2aW5nOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgICAgICBnZXQgc2V0dGxlZCgpIHsgcmV0dXJuIHRoaXMucm9vdC5zZXR0bGVkIH0sXHJcbiAgICAgICAgICAgICAgICAgICAgc2V0IHNldHRsZWQodmFsdWUpIHsgdGhpcy5yb290LnNldHRsZWQgPSB2YWx1ZTsgfSxcclxuICAgICAgICAgICAgICAgICAgICBnZXQgcmVhc29uKCkgeyByZXR1cm4gdGhpcy5yb290LnJlYXNvbiB9XHJcbiAgICAgICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgICAgIGNvbnN0IHJlamVjdG9yID0gcmVqZWN0b3JGb3IocHJvbWlzZSwgbmV3U3RhdGUpO1xyXG4gICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICBSZWZsZWN0LmFwcGx5KHRoZW4sIHZhbHVlLCBbcmVzb2x2ZXJGb3IocHJvbWlzZSwgbmV3U3RhdGUpLCByZWplY3Rvcl0pO1xyXG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0b3IoZXJyKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIHJldHVybjsgLy8gSU1QT1JUQU5UIVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoc3RhdGUuc2V0dGxlZCkgeyByZXR1cm47IH1cclxuICAgICAgICBzdGF0ZS5zZXR0bGVkID0gdHJ1ZTtcclxuICAgICAgICBwcm9taXNlLnJlc29sdmUodmFsdWUpO1xyXG4gICAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgYSBjYWxsYmFjayB0aGF0IGltcGxlbWVudHMgdGhlIHJlamVjdGlvbiBhbGdvcml0aG0gZm9yIHRoZSBnaXZlbiBjYW5jZWxsYWJsZSBwcm9taXNlLlxyXG4gKi9cclxuZnVuY3Rpb24gcmVqZWN0b3JGb3I8VD4ocHJvbWlzZTogQ2FuY2VsbGFibGVQcm9taXNlV2l0aFJlc29sdmVyczxUPiwgc3RhdGU6IENhbmNlbGxhYmxlUHJvbWlzZVN0YXRlKTogQ2FuY2VsbGFibGVQcm9taXNlUmVqZWN0b3Ige1xyXG4gICAgcmV0dXJuIChyZWFzb24/KSA9PiB7XHJcbiAgICAgICAgaWYgKHN0YXRlLnJlc29sdmluZykgeyByZXR1cm47IH1cclxuICAgICAgICBzdGF0ZS5yZXNvbHZpbmcgPSB0cnVlO1xyXG5cclxuICAgICAgICBpZiAoc3RhdGUuc2V0dGxlZCkge1xyXG4gICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgaWYgKHJlYXNvbiBpbnN0YW5jZW9mIENhbmNlbEVycm9yICYmIHN0YXRlLnJlYXNvbiBpbnN0YW5jZW9mIENhbmNlbEVycm9yICYmIE9iamVjdC5pcyhyZWFzb24uY2F1c2UsIHN0YXRlLnJlYXNvbi5jYXVzZSkpIHtcclxuICAgICAgICAgICAgICAgICAgICAvLyBTd2FsbG93IGxhdGUgcmVqZWN0aW9ucyB0aGF0IGFyZSBDYW5jZWxFcnJvcnMgd2hvc2UgY2FuY2VsbGF0aW9uIGNhdXNlIGlzIHRoZSBzYW1lIGFzIG91cnMuXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgICAgICAgICB2b2lkIFByb21pc2UucmVqZWN0KG5ldyBDYW5jZWxsZWRSZWplY3Rpb25FcnJvcihwcm9taXNlLnByb21pc2UsIHJlYXNvbikpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHN0YXRlLnNldHRsZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICBwcm9taXNlLnJlamVjdChyZWFzb24pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENhbmNlbHMgYWxsIHZhbHVlcyBpbiBhbiBhcnJheSB0aGF0IGxvb2sgbGlrZSBjYW5jZWxsYWJsZSB0aGVuYWJsZXMuXHJcbiAqIFJldHVybnMgYSBwcm9taXNlIHRoYXQgZnVsZmlsbHMgb25jZSBhbGwgY2FuY2VsbGF0aW9uIHByb2NlZHVyZXMgZm9yIHRoZSBnaXZlbiB2YWx1ZXMgaGF2ZSBzZXR0bGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gY2FuY2VsQWxsKHBhcmVudDogQ2FuY2VsbGFibGVQcm9taXNlPHVua25vd24+LCB2YWx1ZXM6IGFueVtdLCBjYXVzZT86IGFueSk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgY29uc3QgcmVzdWx0cyA9IFtdO1xyXG5cclxuICAgIGZvciAoY29uc3QgdmFsdWUgb2YgdmFsdWVzKSB7XHJcbiAgICAgICAgbGV0IGNhbmNlbDogQ2FuY2VsbGFibGVQcm9taXNlQ2FuY2VsbGVyO1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGlmICghaXNDYWxsYWJsZSh2YWx1ZS50aGVuKSkgeyBjb250aW51ZTsgfVxyXG4gICAgICAgICAgICBjYW5jZWwgPSB2YWx1ZS5jYW5jZWw7XHJcbiAgICAgICAgICAgIGlmICghaXNDYWxsYWJsZShjYW5jZWwpKSB7IGNvbnRpbnVlOyB9XHJcbiAgICAgICAgfSBjYXRjaCB7IGNvbnRpbnVlOyB9XHJcblxyXG4gICAgICAgIGxldCByZXN1bHQ6IHZvaWQgfCBQcm9taXNlTGlrZTx2b2lkPjtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXN1bHQgPSBSZWZsZWN0LmFwcGx5KGNhbmNlbCwgdmFsdWUsIFtjYXVzZV0pO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgICAgICBQcm9taXNlLnJlamVjdChuZXcgQ2FuY2VsbGVkUmVqZWN0aW9uRXJyb3IocGFyZW50LCBlcnIsIFwiVW5oYW5kbGVkIGV4Y2VwdGlvbiBpbiBjYW5jZWwgbWV0aG9kLlwiKSk7XHJcbiAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKCFyZXN1bHQpIHsgY29udGludWU7IH1cclxuICAgICAgICByZXN1bHRzLnB1c2goXHJcbiAgICAgICAgICAgIChyZXN1bHQgaW5zdGFuY2VvZiBQcm9taXNlICA/IHJlc3VsdCA6IFByb21pc2UucmVzb2x2ZShyZXN1bHQpKS5jYXRjaCgocmVhc29uPykgPT4ge1xyXG4gICAgICAgICAgICAgICAgUHJvbWlzZS5yZWplY3QobmV3IENhbmNlbGxlZFJlamVjdGlvbkVycm9yKHBhcmVudCwgcmVhc29uLCBcIlVuaGFuZGxlZCByZWplY3Rpb24gaW4gY2FuY2VsIG1ldGhvZC5cIikpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIFByb21pc2UuYWxsKHJlc3VsdHMpIGFzIGFueTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgaXRzIGFyZ3VtZW50LlxyXG4gKi9cclxuZnVuY3Rpb24gaWRlbnRpdHk8VD4oeDogVCk6IFQge1xyXG4gICAgcmV0dXJuIHg7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBUaHJvd3MgaXRzIGFyZ3VtZW50LlxyXG4gKi9cclxuZnVuY3Rpb24gdGhyb3dlcihyZWFzb24/OiBhbnkpOiBuZXZlciB7XHJcbiAgICB0aHJvdyByZWFzb247XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRlbXB0cyB2YXJpb3VzIHN0cmF0ZWdpZXMgdG8gY29udmVydCBhbiBlcnJvciB0byBhIHN0cmluZy5cclxuICovXHJcbmZ1bmN0aW9uIGVycm9yTWVzc2FnZShlcnI6IGFueSk6IHN0cmluZyB7XHJcbiAgICB0cnkge1xyXG4gICAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBFcnJvciB8fCB0eXBlb2YgZXJyICE9PSAnb2JqZWN0JyB8fCBlcnIudG9TdHJpbmcgIT09IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcpIHtcclxuICAgICAgICAgICAgcmV0dXJuIFwiXCIgKyBlcnI7XHJcbiAgICAgICAgfVxyXG4gICAgfSBjYXRjaCB7fVxyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGVycik7XHJcbiAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKGVycik7XHJcbiAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgcmV0dXJuIFwiPGNvdWxkIG5vdCBjb252ZXJ0IGVycm9yIHRvIHN0cmluZz5cIjtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGN1cnJlbnQgYmFycmllciBwcm9taXNlIGZvciB0aGUgZ2l2ZW4gY2FuY2VsbGFibGUgcHJvbWlzZS4gSWYgbmVjZXNzYXJ5LCBpbml0aWFsaXNlcyB0aGUgYmFycmllci5cclxuICovXHJcbmZ1bmN0aW9uIGN1cnJlbnRCYXJyaWVyPFQ+KHByb21pc2U6IENhbmNlbGxhYmxlUHJvbWlzZTxUPik6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgbGV0IHB3cjogUGFydGlhbDxQcm9taXNlV2l0aFJlc29sdmVyczx2b2lkPj4gPSBwcm9taXNlW2JhcnJpZXJTeW1dID8/IHt9O1xyXG4gICAgaWYgKCEoJ3Byb21pc2UnIGluIHB3cikpIHtcclxuICAgICAgICBPYmplY3QuYXNzaWduKHB3ciwgcHJvbWlzZVdpdGhSZXNvbHZlcnM8dm9pZD4oKSk7XHJcbiAgICB9XHJcbiAgICBpZiAocHJvbWlzZVtiYXJyaWVyU3ltXSA9PSBudWxsKSB7XHJcbiAgICAgICAgcHdyLnJlc29sdmUhKCk7XHJcbiAgICAgICAgcHJvbWlzZVtiYXJyaWVyU3ltXSA9IHB3cjtcclxuICAgIH1cclxuICAgIHJldHVybiBwd3IucHJvbWlzZSE7XHJcbn1cclxuXHJcbi8vIFBvbHlmaWxsIFByb21pc2Uud2l0aFJlc29sdmVycy5cclxubGV0IHByb21pc2VXaXRoUmVzb2x2ZXJzID0gUHJvbWlzZS53aXRoUmVzb2x2ZXJzO1xyXG5pZiAocHJvbWlzZVdpdGhSZXNvbHZlcnMgJiYgdHlwZW9mIHByb21pc2VXaXRoUmVzb2x2ZXJzID09PSAnZnVuY3Rpb24nKSB7XHJcbiAgICBwcm9taXNlV2l0aFJlc29sdmVycyA9IHByb21pc2VXaXRoUmVzb2x2ZXJzLmJpbmQoUHJvbWlzZSk7XHJcbn0gZWxzZSB7XHJcbiAgICBwcm9taXNlV2l0aFJlc29sdmVycyA9IGZ1bmN0aW9uIDxUPigpOiBQcm9taXNlV2l0aFJlc29sdmVyczxUPiB7XHJcbiAgICAgICAgbGV0IHJlc29sdmUhOiAodmFsdWU6IFQgfCBQcm9taXNlTGlrZTxUPikgPT4gdm9pZDtcclxuICAgICAgICBsZXQgcmVqZWN0ITogKHJlYXNvbj86IGFueSkgPT4gdm9pZDtcclxuICAgICAgICBjb25zdCBwcm9taXNlID0gbmV3IFByb21pc2U8VD4oKHJlcywgcmVqKSA9PiB7IHJlc29sdmUgPSByZXM7IHJlamVjdCA9IHJlajsgfSk7XHJcbiAgICAgICAgcmV0dXJuIHsgcHJvbWlzZSwgcmVzb2x2ZSwgcmVqZWN0IH07XHJcbiAgICB9XHJcbn0iLCAiLypcclxuIF9cdCAgIF9fXHQgIF8gX19cclxufCB8XHQgLyAvX19fIF8oXykgL19fX19cclxufCB8IC98IC8gLyBfXyBgLyAvIC8gX19fL1xyXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcclxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXHJcblRoZSBlbGVjdHJvbiBhbHRlcm5hdGl2ZSBmb3IgR29cclxuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxyXG4qL1xyXG5cclxuaW1wb3J0IHtuZXdSdW50aW1lQ2FsbGVyLCBvYmplY3ROYW1lc30gZnJvbSBcIi4vcnVudGltZS5qc1wiO1xyXG5cclxuY29uc3QgY2FsbCA9IG5ld1J1bnRpbWVDYWxsZXIob2JqZWN0TmFtZXMuQ2xpcGJvYXJkKTtcclxuXHJcbmNvbnN0IENsaXBib2FyZFNldFRleHQgPSAwO1xyXG5jb25zdCBDbGlwYm9hcmRUZXh0ID0gMTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSB0ZXh0IHRvIHRoZSBDbGlwYm9hcmQuXHJcbiAqXHJcbiAqIEBwYXJhbSB0ZXh0IC0gVGhlIHRleHQgdG8gYmUgc2V0IHRvIHRoZSBDbGlwYm9hcmQuXHJcbiAqIEByZXR1cm4gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgb3BlcmF0aW9uIGlzIHN1Y2Nlc3NmdWwuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gU2V0VGV4dCh0ZXh0OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIHJldHVybiBjYWxsKENsaXBib2FyZFNldFRleHQsIHt0ZXh0fSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIENsaXBib2FyZCB0ZXh0XHJcbiAqXHJcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIHRleHQgZnJvbSB0aGUgQ2xpcGJvYXJkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIFRleHQoKTogUHJvbWlzZTxzdHJpbmc+IHtcclxuICAgIHJldHVybiBjYWxsKENsaXBib2FyZFRleHQpO1xyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG4vKipcclxuICogQW55IGlzIGEgZHVtbXkgY3JlYXRpb24gZnVuY3Rpb24gZm9yIHNpbXBsZSBvciB1bmtub3duIHR5cGVzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIEFueTxUID0gYW55Pihzb3VyY2U6IGFueSk6IFQge1xyXG4gICAgcmV0dXJuIHNvdXJjZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEJ5dGVTbGljZSBpcyBhIGNyZWF0aW9uIGZ1bmN0aW9uIHRoYXQgcmVwbGFjZXNcclxuICogbnVsbCBzdHJpbmdzIHdpdGggZW1wdHkgc3RyaW5ncy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBCeXRlU2xpY2Uoc291cmNlOiBhbnkpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuICgoc291cmNlID09IG51bGwpID8gXCJcIiA6IHNvdXJjZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBcnJheSB0YWtlcyBhIGNyZWF0aW9uIGZ1bmN0aW9uIGZvciBhbiBhcmJpdHJhcnkgdHlwZVxyXG4gKiBhbmQgcmV0dXJucyBhbiBpbi1wbGFjZSBjcmVhdGlvbiBmdW5jdGlvbiBmb3IgYW4gYXJyYXlcclxuICogd2hvc2UgZWxlbWVudHMgYXJlIG9mIHRoYXQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBBcnJheTxUID0gYW55PihlbGVtZW50OiAoc291cmNlOiBhbnkpID0+IFQpOiAoc291cmNlOiBhbnkpID0+IFRbXSB7XHJcbiAgICBpZiAoZWxlbWVudCA9PT0gQW55KSB7XHJcbiAgICAgICAgcmV0dXJuIChzb3VyY2UpID0+IChzb3VyY2UgPT09IG51bGwgPyBbXSA6IHNvdXJjZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIChzb3VyY2UpID0+IHtcclxuICAgICAgICBpZiAoc291cmNlID09PSBudWxsKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBbXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzb3VyY2UubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgICAgc291cmNlW2ldID0gZWxlbWVudChzb3VyY2VbaV0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gc291cmNlO1xyXG4gICAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcCB0YWtlcyBjcmVhdGlvbiBmdW5jdGlvbnMgZm9yIHR3byBhcmJpdHJhcnkgdHlwZXNcclxuICogYW5kIHJldHVybnMgYW4gaW4tcGxhY2UgY3JlYXRpb24gZnVuY3Rpb24gZm9yIGFuIG9iamVjdFxyXG4gKiB3aG9zZSBrZXlzIGFuZCB2YWx1ZXMgYXJlIG9mIHRob3NlIHR5cGVzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIE1hcDxWID0gYW55PihrZXk6IChzb3VyY2U6IGFueSkgPT4gc3RyaW5nLCB2YWx1ZTogKHNvdXJjZTogYW55KSA9PiBWKTogKHNvdXJjZTogYW55KSA9PiBSZWNvcmQ8c3RyaW5nLCBWPiB7XHJcbiAgICBpZiAodmFsdWUgPT09IEFueSkge1xyXG4gICAgICAgIHJldHVybiAoc291cmNlKSA9PiAoc291cmNlID09PSBudWxsID8ge30gOiBzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiAoc291cmNlKSA9PiB7XHJcbiAgICAgICAgaWYgKHNvdXJjZSA9PT0gbnVsbCkge1xyXG4gICAgICAgICAgICByZXR1cm4ge307XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIHNvdXJjZSkge1xyXG4gICAgICAgICAgICBzb3VyY2Vba2V5XSA9IHZhbHVlKHNvdXJjZVtrZXldKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHNvdXJjZTtcclxuICAgIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBOdWxsYWJsZSB0YWtlcyBhIGNyZWF0aW9uIGZ1bmN0aW9uIGZvciBhbiBhcmJpdHJhcnkgdHlwZVxyXG4gKiBhbmQgcmV0dXJucyBhIGNyZWF0aW9uIGZ1bmN0aW9uIGZvciBhIG51bGxhYmxlIHZhbHVlIG9mIHRoYXQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBOdWxsYWJsZTxUID0gYW55PihlbGVtZW50OiAoc291cmNlOiBhbnkpID0+IFQpOiAoc291cmNlOiBhbnkpID0+IChUIHwgbnVsbCkge1xyXG4gICAgaWYgKGVsZW1lbnQgPT09IEFueSkge1xyXG4gICAgICAgIHJldHVybiBBbnk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIChzb3VyY2UpID0+IChzb3VyY2UgPT09IG51bGwgPyBudWxsIDogZWxlbWVudChzb3VyY2UpKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0cnVjdCB0YWtlcyBhbiBvYmplY3QgbWFwcGluZyBmaWVsZCBuYW1lcyB0byBjcmVhdGlvbiBmdW5jdGlvbnNcclxuICogYW5kIHJldHVybnMgYW4gaW4tcGxhY2UgY3JlYXRpb24gZnVuY3Rpb24gZm9yIGEgc3RydWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIFN0cnVjdChjcmVhdGVGaWVsZDogUmVjb3JkPHN0cmluZywgKHNvdXJjZTogYW55KSA9PiBhbnk+KTpcclxuICAgIDxVIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgYW55PiA9IGFueT4oc291cmNlOiBhbnkpID0+IFVcclxue1xyXG4gICAgbGV0IGFsbEFueSA9IHRydWU7XHJcbiAgICBmb3IgKGNvbnN0IG5hbWUgaW4gY3JlYXRlRmllbGQpIHtcclxuICAgICAgICBpZiAoY3JlYXRlRmllbGRbbmFtZV0gIT09IEFueSkge1xyXG4gICAgICAgICAgICBhbGxBbnkgPSBmYWxzZTtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgaWYgKGFsbEFueSkge1xyXG4gICAgICAgIHJldHVybiBBbnk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIChzb3VyY2UpID0+IHtcclxuICAgICAgICBmb3IgKGNvbnN0IG5hbWUgaW4gY3JlYXRlRmllbGQpIHtcclxuICAgICAgICAgICAgaWYgKG5hbWUgaW4gc291cmNlKSB7XHJcbiAgICAgICAgICAgICAgICBzb3VyY2VbbmFtZV0gPSBjcmVhdGVGaWVsZFtuYW1lXShzb3VyY2VbbmFtZV0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBzb3VyY2U7XHJcbiAgICB9O1xyXG59XHJcbiIsICIvKlxyXG4gX1x0ICAgX19cdCAgXyBfX1xyXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xyXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXHJcbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxyXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cclxuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xyXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XHJcbiovXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFNpemUge1xyXG4gICAgLyoqIFRoZSB3aWR0aCBvZiBhIHJlY3Rhbmd1bGFyIGFyZWEuICovXHJcbiAgICBXaWR0aDogbnVtYmVyO1xyXG4gICAgLyoqIFRoZSBoZWlnaHQgb2YgYSByZWN0YW5ndWxhciBhcmVhLiAqL1xyXG4gICAgSGVpZ2h0OiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUmVjdCB7XHJcbiAgICAvKiogVGhlIFggY29vcmRpbmF0ZSBvZiB0aGUgb3JpZ2luLiAqL1xyXG4gICAgWDogbnVtYmVyO1xyXG4gICAgLyoqIFRoZSBZIGNvb3JkaW5hdGUgb2YgdGhlIG9yaWdpbi4gKi9cclxuICAgIFk6IG51bWJlcjtcclxuICAgIC8qKiBUaGUgd2lkdGggb2YgdGhlIHJlY3RhbmdsZS4gKi9cclxuICAgIFdpZHRoOiBudW1iZXI7XHJcbiAgICAvKiogVGhlIGhlaWdodCBvZiB0aGUgcmVjdGFuZ2xlLiAqL1xyXG4gICAgSGVpZ2h0OiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgU2NyZWVuIHtcclxuICAgIC8qKiBVbmlxdWUgaWRlbnRpZmllciBmb3IgdGhlIHNjcmVlbi4gKi9cclxuICAgIElEOiBzdHJpbmc7XHJcbiAgICAvKiogSHVtYW4tcmVhZGFibGUgbmFtZSBvZiB0aGUgc2NyZWVuLiAqL1xyXG4gICAgTmFtZTogc3RyaW5nO1xyXG4gICAgLyoqIFRoZSBzY2FsZSBmYWN0b3Igb2YgdGhlIHNjcmVlbiAoRFBJLzk2KS4gMSA9IHN0YW5kYXJkIERQSSwgMiA9IEhpRFBJIChSZXRpbmEpLCBldGMuICovXHJcbiAgICBTY2FsZUZhY3RvcjogbnVtYmVyO1xyXG4gICAgLyoqIFRoZSBYIGNvb3JkaW5hdGUgb2YgdGhlIHNjcmVlbi4gKi9cclxuICAgIFg6IG51bWJlcjtcclxuICAgIC8qKiBUaGUgWSBjb29yZGluYXRlIG9mIHRoZSBzY3JlZW4uICovXHJcbiAgICBZOiBudW1iZXI7XHJcbiAgICAvKiogQ29udGFpbnMgdGhlIHdpZHRoIGFuZCBoZWlnaHQgb2YgdGhlIHNjcmVlbi4gKi9cclxuICAgIFNpemU6IFNpemU7XHJcbiAgICAvKiogQ29udGFpbnMgdGhlIGJvdW5kcyBvZiB0aGUgc2NyZWVuIGluIHRlcm1zIG9mIFgsIFksIFdpZHRoLCBhbmQgSGVpZ2h0LiAqL1xyXG4gICAgQm91bmRzOiBSZWN0O1xyXG4gICAgLyoqIENvbnRhaW5zIHRoZSBwaHlzaWNhbCBib3VuZHMgb2YgdGhlIHNjcmVlbiBpbiB0ZXJtcyBvZiBYLCBZLCBXaWR0aCwgYW5kIEhlaWdodCAoYmVmb3JlIHNjYWxpbmcpLiAqL1xyXG4gICAgUGh5c2ljYWxCb3VuZHM6IFJlY3Q7XHJcbiAgICAvKiogQ29udGFpbnMgdGhlIGFyZWEgb2YgdGhlIHNjcmVlbiB0aGF0IGlzIGFjdHVhbGx5IHVzYWJsZSAoZXhjbHVkaW5nIHRhc2tiYXIgYW5kIG90aGVyIHN5c3RlbSBVSSkuICovXHJcbiAgICBXb3JrQXJlYTogUmVjdDtcclxuICAgIC8qKiBDb250YWlucyB0aGUgcGh5c2ljYWwgV29ya0FyZWEgb2YgdGhlIHNjcmVlbiAoYmVmb3JlIHNjYWxpbmcpLiAqL1xyXG4gICAgUGh5c2ljYWxXb3JrQXJlYTogUmVjdDtcclxuICAgIC8qKiBUcnVlIGlmIHRoaXMgaXMgdGhlIHByaW1hcnkgbW9uaXRvciBzZWxlY3RlZCBieSB0aGUgdXNlciBpbiB0aGUgb3BlcmF0aW5nIHN5c3RlbS4gKi9cclxuICAgIElzUHJpbWFyeTogYm9vbGVhbjtcclxuICAgIC8qKiBUaGUgcm90YXRpb24gb2YgdGhlIHNjcmVlbi4gKi9cclxuICAgIFJvdGF0aW9uOiBudW1iZXI7XHJcbn1cclxuXHJcbmltcG9ydCB7IG5ld1J1bnRpbWVDYWxsZXIsIG9iamVjdE5hbWVzIH0gZnJvbSBcIi4vcnVudGltZS5qc1wiO1xyXG5jb25zdCBjYWxsID0gbmV3UnVudGltZUNhbGxlcihvYmplY3ROYW1lcy5TY3JlZW5zKTtcclxuXHJcbmNvbnN0IGdldEFsbCA9IDA7XHJcbmNvbnN0IGdldFByaW1hcnkgPSAxO1xyXG5jb25zdCBnZXRDdXJyZW50ID0gMjtcclxuXHJcbi8qKlxyXG4gKiBHZXRzIGFsbCBzY3JlZW5zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBhcnJheSBvZiBTY3JlZW4gb2JqZWN0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBHZXRBbGwoKTogUHJvbWlzZTxTY3JlZW5bXT4ge1xyXG4gICAgcmV0dXJuIGNhbGwoZ2V0QWxsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIHByaW1hcnkgc2NyZWVuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcHJpbWFyeSBzY3JlZW4uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gR2V0UHJpbWFyeSgpOiBQcm9taXNlPFNjcmVlbj4ge1xyXG4gICAgcmV0dXJuIGNhbGwoZ2V0UHJpbWFyeSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBjdXJyZW50IGFjdGl2ZSBzY3JlZW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIGN1cnJlbnQgYWN0aXZlIHNjcmVlbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBHZXRDdXJyZW50KCk6IFByb21pc2U8U2NyZWVuPiB7XHJcbiAgICByZXR1cm4gY2FsbChnZXRDdXJyZW50KTtcclxufVxyXG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOzs7QUNBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOzs7QUNBQTtBQUFBO0FBQUE7QUFBQTs7O0FDNkJBLElBQU0sY0FDRjtBQUVHLFNBQVMsT0FBTyxPQUFlLElBQVk7QUFDOUMsTUFBSSxLQUFLO0FBRVQsTUFBSSxJQUFJLE9BQU87QUFDZixTQUFPLEtBQUs7QUFFUixVQUFNLFlBQWEsS0FBSyxPQUFPLElBQUksS0FBTSxDQUFDO0FBQUEsRUFDOUM7QUFDQSxTQUFPO0FBQ1g7OztBQzdCQSxJQUFNLGFBQWEsT0FBTyxTQUFTLFNBQVM7QUFHckMsSUFBTSxjQUFjLE9BQU8sT0FBTztBQUFBLEVBQ3JDLE1BQU07QUFBQSxFQUNOLFdBQVc7QUFBQSxFQUNYLGFBQWE7QUFBQSxFQUNiLFFBQVE7QUFBQSxFQUNSLGFBQWE7QUFBQSxFQUNiLFFBQVE7QUFBQSxFQUNSLFFBQVE7QUFBQSxFQUNSLFNBQVM7QUFBQSxFQUNULFFBQVE7QUFBQSxFQUNSLFNBQVM7QUFBQSxFQUNULFlBQVk7QUFDaEIsQ0FBQztBQUNNLElBQUksV0FBVyxPQUFPO0FBU3RCLFNBQVMsaUJBQWlCLFFBQWdCLGFBQXFCLElBQUk7QUFDdEUsU0FBTyxTQUFVLFFBQWdCLE9BQVksTUFBTTtBQUMvQyxXQUFPLGtCQUFrQixRQUFRLFFBQVEsWUFBWSxJQUFJO0FBQUEsRUFDN0Q7QUFDSjtBQUVBLGVBQWUsa0JBQWtCLFVBQWtCLFFBQWdCLFlBQW9CLE1BQXlCO0FBM0NoSCxNQUFBQSxLQUFBO0FBNENJLE1BQUksTUFBTSxJQUFJLElBQUksVUFBVTtBQUM1QixNQUFJLGFBQWEsT0FBTyxVQUFVLFNBQVMsU0FBUyxDQUFDO0FBQ3JELE1BQUksYUFBYSxPQUFPLFVBQVUsT0FBTyxTQUFTLENBQUM7QUFDbkQsTUFBSSxNQUFNO0FBQUUsUUFBSSxhQUFhLE9BQU8sUUFBUSxLQUFLLFVBQVUsSUFBSSxDQUFDO0FBQUEsRUFBRztBQUVuRSxNQUFJLFVBQWtDO0FBQUEsSUFDbEMsQ0FBQyxtQkFBbUIsR0FBRztBQUFBLEVBQzNCO0FBQ0EsTUFBSSxZQUFZO0FBQ1osWUFBUSxxQkFBcUIsSUFBSTtBQUFBLEVBQ3JDO0FBRUEsTUFBSSxXQUFXLE1BQU0sTUFBTSxLQUFLLEVBQUUsUUFBUSxDQUFDO0FBQzNDLE1BQUksQ0FBQyxTQUFTLElBQUk7QUFDZCxVQUFNLElBQUksTUFBTSxNQUFNLFNBQVMsS0FBSyxDQUFDO0FBQUEsRUFDekM7QUFFQSxRQUFLLE1BQUFBLE1BQUEsU0FBUyxRQUFRLElBQUksY0FBYyxNQUFuQyxnQkFBQUEsSUFBc0MsUUFBUSx3QkFBOUMsWUFBcUUsUUFBUSxJQUFJO0FBQ2xGLFdBQU8sU0FBUyxLQUFLO0FBQUEsRUFDekIsT0FBTztBQUNILFdBQU8sU0FBUyxLQUFLO0FBQUEsRUFDekI7QUFDSjs7O0FGdERBLElBQU0sT0FBTyxpQkFBaUIsWUFBWSxPQUFPO0FBRWpELElBQU0saUJBQWlCO0FBT2hCLFNBQVMsUUFBUSxLQUFrQztBQUN0RCxTQUFPLEtBQUssZ0JBQWdCLEVBQUMsS0FBSyxJQUFJLFNBQVMsRUFBQyxDQUFDO0FBQ3JEOzs7QUd2QkE7QUFBQTtBQUFBLGVBQUFDO0FBQUEsRUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFjQSxPQUFPLFNBQVMsT0FBTyxVQUFVLENBQUM7QUFDbEMsT0FBTyxPQUFPLHNCQUFzQjtBQUNwQyxPQUFPLE9BQU8sdUJBQXVCO0FBSXJDLElBQU1DLFFBQU8saUJBQWlCLFlBQVksTUFBTTtBQUNoRCxJQUFNLGtCQUFrQixvQkFBSSxJQUE4QjtBQUcxRCxJQUFNLGFBQWE7QUFDbkIsSUFBTSxnQkFBZ0I7QUFDdEIsSUFBTSxjQUFjO0FBQ3BCLElBQU0saUJBQWlCO0FBQ3ZCLElBQU0saUJBQWlCO0FBQ3ZCLElBQU0saUJBQWlCO0FBMEd2QixTQUFTLHFCQUFxQixJQUFZLE1BQWMsUUFBdUI7QUFDM0UsTUFBSSxZQUFZLHFCQUFxQixFQUFFO0FBQ3ZDLE1BQUksQ0FBQyxXQUFXO0FBQ1o7QUFBQSxFQUNKO0FBRUEsTUFBSSxRQUFRO0FBQ1IsUUFBSTtBQUNBLGdCQUFVLFFBQVEsS0FBSyxNQUFNLElBQUksQ0FBQztBQUFBLElBQ3RDLFNBQVMsS0FBVTtBQUNmLGdCQUFVLE9BQU8sSUFBSSxVQUFVLDZCQUE2QixJQUFJLFNBQVMsRUFBRSxPQUFPLElBQUksQ0FBQyxDQUFDO0FBQUEsSUFDNUY7QUFBQSxFQUNKLE9BQU87QUFDSCxjQUFVLFFBQVEsSUFBSTtBQUFBLEVBQzFCO0FBQ0o7QUFRQSxTQUFTLG9CQUFvQixJQUFZLFNBQXVCO0FBOUpoRSxNQUFBQztBQStKSSxHQUFBQSxNQUFBLHFCQUFxQixFQUFFLE1BQXZCLGdCQUFBQSxJQUEwQixPQUFPLElBQUksT0FBTyxNQUFNLE9BQU87QUFDN0Q7QUFRQSxTQUFTLHFCQUFxQixJQUEwQztBQUNwRSxRQUFNLFdBQVcsZ0JBQWdCLElBQUksRUFBRTtBQUN2QyxrQkFBZ0IsT0FBTyxFQUFFO0FBQ3pCLFNBQU87QUFDWDtBQU9BLFNBQVMsYUFBcUI7QUFDMUIsTUFBSTtBQUNKLEtBQUc7QUFDQyxhQUFTLE9BQU87QUFBQSxFQUNwQixTQUFTLGdCQUFnQixJQUFJLE1BQU07QUFDbkMsU0FBTztBQUNYO0FBU0EsU0FBUyxPQUFPLE1BQWMsVUFBZ0YsQ0FBQyxHQUFpQjtBQUM1SCxRQUFNLEtBQUssV0FBVztBQUN0QixTQUFPLElBQUksUUFBUSxDQUFDLFNBQVMsV0FBVztBQUNwQyxvQkFBZ0IsSUFBSSxJQUFJLEVBQUUsU0FBUyxPQUFPLENBQUM7QUFDM0MsSUFBQUQsTUFBSyxNQUFNLE9BQU8sT0FBTyxFQUFFLGFBQWEsR0FBRyxHQUFHLE9BQU8sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxRQUFhO0FBQ3hFLHNCQUFnQixPQUFPLEVBQUU7QUFDekIsYUFBTyxHQUFHO0FBQUEsSUFDZCxDQUFDO0FBQUEsRUFDTCxDQUFDO0FBQ0w7QUFRTyxTQUFTLEtBQUssU0FBZ0Q7QUFBRSxTQUFPLE9BQU8sWUFBWSxPQUFPO0FBQUc7QUFRcEcsU0FBUyxRQUFRLFNBQWdEO0FBQUUsU0FBTyxPQUFPLGVBQWUsT0FBTztBQUFHO0FBUTFHLFNBQVNFLE9BQU0sU0FBZ0Q7QUFBRSxTQUFPLE9BQU8sYUFBYSxPQUFPO0FBQUc7QUFRdEcsU0FBUyxTQUFTLFNBQWdEO0FBQUUsU0FBTyxPQUFPLGdCQUFnQixPQUFPO0FBQUc7QUFXNUcsU0FBUyxTQUFTLFNBQTREO0FBdFByRixNQUFBRDtBQXNQdUYsVUFBT0EsTUFBQSxPQUFPLGdCQUFnQixPQUFPLE1BQTlCLE9BQUFBLE1BQW1DLENBQUM7QUFBRztBQVE5SCxTQUFTLFNBQVMsU0FBaUQ7QUFBRSxTQUFPLE9BQU8sZ0JBQWdCLE9BQU87QUFBRzs7O0FDOVBwSDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOzs7QUNhTyxJQUFNLGlCQUFpQixvQkFBSSxJQUF3QjtBQUVuRCxJQUFNLFdBQU4sTUFBZTtBQUFBLEVBS2xCLFlBQVksV0FBbUIsVUFBK0IsY0FBc0I7QUFDaEYsU0FBSyxZQUFZO0FBQ2pCLFNBQUssV0FBVztBQUNoQixTQUFLLGVBQWUsZ0JBQWdCO0FBQUEsRUFDeEM7QUFBQSxFQUVBLFNBQVMsTUFBb0I7QUFDekIsUUFBSTtBQUNBLFdBQUssU0FBUyxJQUFJO0FBQUEsSUFDdEIsU0FBUyxLQUFLO0FBQ1YsY0FBUSxNQUFNLEdBQUc7QUFBQSxJQUNyQjtBQUVBLFFBQUksS0FBSyxpQkFBaUIsR0FBSSxRQUFPO0FBQ3JDLFNBQUssZ0JBQWdCO0FBQ3JCLFdBQU8sS0FBSyxpQkFBaUI7QUFBQSxFQUNqQztBQUNKO0FBRU8sU0FBUyxZQUFZLFVBQTBCO0FBQ2xELE1BQUksWUFBWSxlQUFlLElBQUksU0FBUyxTQUFTO0FBQ3JELE1BQUksQ0FBQyxXQUFXO0FBQ1o7QUFBQSxFQUNKO0FBRUEsY0FBWSxVQUFVLE9BQU8sT0FBSyxNQUFNLFFBQVE7QUFDaEQsTUFBSSxVQUFVLFdBQVcsR0FBRztBQUN4QixtQkFBZSxPQUFPLFNBQVMsU0FBUztBQUFBLEVBQzVDLE9BQU87QUFDSCxtQkFBZSxJQUFJLFNBQVMsV0FBVyxTQUFTO0FBQUEsRUFDcEQ7QUFDSjs7O0FDdENPLElBQU0sUUFBUSxPQUFPLE9BQU87QUFBQSxFQUNsQyxTQUFTLE9BQU8sT0FBTztBQUFBLElBQ3RCLHVCQUF1QjtBQUFBLElBQ3ZCLHNCQUFzQjtBQUFBLElBQ3RCLG9CQUFvQjtBQUFBLElBQ3BCLGtCQUFrQjtBQUFBLElBQ2xCLFlBQVk7QUFBQSxJQUNaLG9CQUFvQjtBQUFBLElBQ3BCLG9CQUFvQjtBQUFBLElBQ3BCLDRCQUE0QjtBQUFBLElBQzVCLGNBQWM7QUFBQSxJQUNkLHVCQUF1QjtBQUFBLElBQ3ZCLG1CQUFtQjtBQUFBLElBQ25CLGVBQWU7QUFBQSxJQUNmLGVBQWU7QUFBQSxJQUNmLGlCQUFpQjtBQUFBLElBQ2pCLGtCQUFrQjtBQUFBLElBQ2xCLGdCQUFnQjtBQUFBLElBQ2hCLGlCQUFpQjtBQUFBLElBQ2pCLGlCQUFpQjtBQUFBLElBQ2pCLGdCQUFnQjtBQUFBLElBQ2hCLGVBQWU7QUFBQSxJQUNmLGlCQUFpQjtBQUFBLElBQ2pCLGtCQUFrQjtBQUFBLElBQ2xCLFlBQVk7QUFBQSxJQUNaLGdCQUFnQjtBQUFBLElBQ2hCLGVBQWU7QUFBQSxJQUNmLGFBQWE7QUFBQSxJQUNiLGlCQUFpQjtBQUFBLElBQ2pCLG9CQUFvQjtBQUFBLElBQ3BCLDBCQUEwQjtBQUFBLElBQzFCLDJCQUEyQjtBQUFBLElBQzNCLDBCQUEwQjtBQUFBLElBQzFCLHdCQUF3QjtBQUFBLElBQ3hCLGFBQWE7QUFBQSxJQUNiLGVBQWU7QUFBQSxJQUNmLGdCQUFnQjtBQUFBLElBQ2hCLFlBQVk7QUFBQSxJQUNaLGlCQUFpQjtBQUFBLElBQ2pCLG1CQUFtQjtBQUFBLElBQ25CLG9CQUFvQjtBQUFBLElBQ3BCLHFCQUFxQjtBQUFBLElBQ3JCLGdCQUFnQjtBQUFBLElBQ2hCLGtCQUFrQjtBQUFBLElBQ2xCLGdCQUFnQjtBQUFBLElBQ2hCLGtCQUFrQjtBQUFBLEVBQ25CLENBQUM7QUFBQSxFQUNELEtBQUssT0FBTyxPQUFPO0FBQUEsSUFDbEIsNEJBQTRCO0FBQUEsSUFDNUIsdUNBQXVDO0FBQUEsSUFDdkMseUNBQXlDO0FBQUEsSUFDekMsMEJBQTBCO0FBQUEsSUFDMUIsb0NBQW9DO0FBQUEsSUFDcEMsc0NBQXNDO0FBQUEsSUFDdEMsb0NBQW9DO0FBQUEsSUFDcEMsMENBQTBDO0FBQUEsSUFDMUMsMkJBQTJCO0FBQUEsSUFDM0IsK0JBQStCO0FBQUEsSUFDL0Isb0JBQW9CO0FBQUEsSUFDcEIsNEJBQTRCO0FBQUEsSUFDNUIsc0JBQXNCO0FBQUEsSUFDdEIsc0JBQXNCO0FBQUEsSUFDdEIsK0JBQStCO0FBQUEsSUFDL0IsNkJBQTZCO0FBQUEsSUFDN0IsZ0NBQWdDO0FBQUEsSUFDaEMscUJBQXFCO0FBQUEsSUFDckIsNkJBQTZCO0FBQUEsSUFDN0IsMEJBQTBCO0FBQUEsSUFDMUIsdUJBQXVCO0FBQUEsSUFDdkIsdUJBQXVCO0FBQUEsSUFDdkIsZ0JBQWdCO0FBQUEsSUFDaEIsc0JBQXNCO0FBQUEsSUFDdEIsY0FBYztBQUFBLElBQ2Qsb0JBQW9CO0FBQUEsSUFDcEIsb0JBQW9CO0FBQUEsSUFDcEIsc0JBQXNCO0FBQUEsSUFDdEIsYUFBYTtBQUFBLElBQ2IsY0FBYztBQUFBLElBQ2QsbUJBQW1CO0FBQUEsSUFDbkIsbUJBQW1CO0FBQUEsSUFDbkIseUJBQXlCO0FBQUEsSUFDekIsZUFBZTtBQUFBLElBQ2YsaUJBQWlCO0FBQUEsSUFDakIsdUJBQXVCO0FBQUEsSUFDdkIscUJBQXFCO0FBQUEsSUFDckIscUJBQXFCO0FBQUEsSUFDckIsdUJBQXVCO0FBQUEsSUFDdkIsY0FBYztBQUFBLElBQ2QsZUFBZTtBQUFBLElBQ2Ysb0JBQW9CO0FBQUEsSUFDcEIsb0JBQW9CO0FBQUEsSUFDcEIsMEJBQTBCO0FBQUEsSUFDMUIsZ0JBQWdCO0FBQUEsSUFDaEIsNEJBQTRCO0FBQUEsSUFDNUIsNEJBQTRCO0FBQUEsSUFDNUIseURBQXlEO0FBQUEsSUFDekQsc0NBQXNDO0FBQUEsSUFDdEMsb0JBQW9CO0FBQUEsSUFDcEIscUJBQXFCO0FBQUEsSUFDckIscUJBQXFCO0FBQUEsSUFDckIsc0JBQXNCO0FBQUEsSUFDdEIsZ0NBQWdDO0FBQUEsSUFDaEMsa0NBQWtDO0FBQUEsSUFDbEMsbUNBQW1DO0FBQUEsSUFDbkMsb0NBQW9DO0FBQUEsSUFDcEMsK0JBQStCO0FBQUEsSUFDL0IsNkJBQTZCO0FBQUEsSUFDN0IsdUJBQXVCO0FBQUEsSUFDdkIsaUNBQWlDO0FBQUEsSUFDakMsOEJBQThCO0FBQUEsSUFDOUIsNEJBQTRCO0FBQUEsSUFDNUIsc0NBQXNDO0FBQUEsSUFDdEMsNEJBQTRCO0FBQUEsSUFDNUIsc0JBQXNCO0FBQUEsSUFDdEIsa0NBQWtDO0FBQUEsSUFDbEMsc0JBQXNCO0FBQUEsSUFDdEIsd0JBQXdCO0FBQUEsSUFDeEIsd0JBQXdCO0FBQUEsSUFDeEIsbUJBQW1CO0FBQUEsSUFDbkIsMEJBQTBCO0FBQUEsSUFDMUIsOEJBQThCO0FBQUEsSUFDOUIseUJBQXlCO0FBQUEsSUFDekIsNkJBQTZCO0FBQUEsSUFDN0IsaUJBQWlCO0FBQUEsSUFDakIsZ0JBQWdCO0FBQUEsSUFDaEIsc0JBQXNCO0FBQUEsSUFDdEIsZUFBZTtBQUFBLElBQ2YseUJBQXlCO0FBQUEsSUFDekIsd0JBQXdCO0FBQUEsSUFDeEIsb0JBQW9CO0FBQUEsSUFDcEIscUJBQXFCO0FBQUEsSUFDckIsaUJBQWlCO0FBQUEsSUFDakIsaUJBQWlCO0FBQUEsSUFDakIsc0JBQXNCO0FBQUEsSUFDdEIsbUNBQW1DO0FBQUEsSUFDbkMscUNBQXFDO0FBQUEsSUFDckMsdUJBQXVCO0FBQUEsSUFDdkIsc0JBQXNCO0FBQUEsSUFDdEIsd0JBQXdCO0FBQUEsSUFDeEIsZUFBZTtBQUFBLElBQ2YsMkJBQTJCO0FBQUEsSUFDM0IsMEJBQTBCO0FBQUEsSUFDMUIsNkJBQTZCO0FBQUEsSUFDN0IsWUFBWTtBQUFBLElBQ1osZ0JBQWdCO0FBQUEsSUFDaEIsa0JBQWtCO0FBQUEsSUFDbEIsZ0JBQWdCO0FBQUEsSUFDaEIsa0JBQWtCO0FBQUEsSUFDbEIsbUJBQW1CO0FBQUEsSUFDbkIsWUFBWTtBQUFBLElBQ1oscUJBQXFCO0FBQUEsSUFDckIsc0JBQXNCO0FBQUEsSUFDdEIsc0JBQXNCO0FBQUEsSUFDdEIsOEJBQThCO0FBQUEsSUFDOUIsaUJBQWlCO0FBQUEsSUFDakIseUJBQXlCO0FBQUEsSUFDekIsMkJBQTJCO0FBQUEsSUFDM0IsK0JBQStCO0FBQUEsSUFDL0IsMEJBQTBCO0FBQUEsSUFDMUIsOEJBQThCO0FBQUEsSUFDOUIsaUJBQWlCO0FBQUEsSUFDakIsdUJBQXVCO0FBQUEsSUFDdkIsZ0JBQWdCO0FBQUEsSUFDaEIsMEJBQTBCO0FBQUEsSUFDMUIseUJBQXlCO0FBQUEsSUFDekIsc0JBQXNCO0FBQUEsSUFDdEIsa0JBQWtCO0FBQUEsSUFDbEIsbUJBQW1CO0FBQUEsSUFDbkIsa0JBQWtCO0FBQUEsSUFDbEIsdUJBQXVCO0FBQUEsSUFDdkIsb0NBQW9DO0FBQUEsSUFDcEMsc0NBQXNDO0FBQUEsSUFDdEMsd0JBQXdCO0FBQUEsSUFDeEIsdUJBQXVCO0FBQUEsSUFDdkIseUJBQXlCO0FBQUEsSUFDekIsNEJBQTRCO0FBQUEsSUFDNUIsNEJBQTRCO0FBQUEsSUFDNUIsY0FBYztBQUFBLElBQ2QsZUFBZTtBQUFBLElBQ2YsaUJBQWlCO0FBQUEsRUFDbEIsQ0FBQztBQUFBLEVBQ0QsT0FBTyxPQUFPLE9BQU87QUFBQSxJQUNwQixvQkFBb0I7QUFBQSxJQUNwQixvQkFBb0I7QUFBQSxJQUNwQixtQkFBbUI7QUFBQSxJQUNuQixlQUFlO0FBQUEsSUFDZixpQkFBaUI7QUFBQSxJQUNqQixlQUFlO0FBQUEsSUFDZixnQkFBZ0I7QUFBQSxJQUNoQixtQkFBbUI7QUFBQSxFQUNwQixDQUFDO0FBQUEsRUFDRCxRQUFRLE9BQU8sT0FBTztBQUFBLElBQ3JCLDJCQUEyQjtBQUFBLElBQzNCLG9CQUFvQjtBQUFBLElBQ3BCLDRCQUE0QjtBQUFBLElBQzVCLGNBQWM7QUFBQSxJQUNkLGVBQWU7QUFBQSxJQUNmLGVBQWU7QUFBQSxJQUNmLGlCQUFpQjtBQUFBLElBQ2pCLGtCQUFrQjtBQUFBLElBQ2xCLG9CQUFvQjtBQUFBLElBQ3BCLGFBQWE7QUFBQSxJQUNiLGtCQUFrQjtBQUFBLElBQ2xCLFlBQVk7QUFBQSxJQUNaLGlCQUFpQjtBQUFBLElBQ2pCLGdCQUFnQjtBQUFBLElBQ2hCLGdCQUFnQjtBQUFBLElBQ2hCLHVCQUF1QjtBQUFBLElBQ3ZCLGVBQWU7QUFBQSxJQUNmLG9CQUFvQjtBQUFBLElBQ3BCLFlBQVk7QUFBQSxJQUNaLG9CQUFvQjtBQUFBLElBQ3BCLGtCQUFrQjtBQUFBLElBQ2xCLGtCQUFrQjtBQUFBLElBQ2xCLFlBQVk7QUFBQSxJQUNaLGNBQWM7QUFBQSxJQUNkLGVBQWU7QUFBQSxJQUNmLGlCQUFpQjtBQUFBLElBQ2pCLDRCQUE0QjtBQUFBLEVBQzdCLENBQUM7QUFDRixDQUFDOzs7QUYzTkQsT0FBTyxTQUFTLE9BQU8sVUFBVSxDQUFDO0FBQ2xDLE9BQU8sT0FBTyxxQkFBcUI7QUFFbkMsSUFBTUUsUUFBTyxpQkFBaUIsWUFBWSxNQUFNO0FBQ2hELElBQU0sYUFBYTtBQVlaLElBQU0sYUFBTixNQUFpQjtBQUFBLEVBaUJwQixZQUFZLE1BQWMsT0FBWSxNQUFNO0FBQ3hDLFNBQUssT0FBTztBQUNaLFNBQUssT0FBTztBQUFBLEVBQ2hCO0FBQ0o7QUFFQSxTQUFTLG1CQUFtQixPQUFZO0FBQ3BDLE1BQUksWUFBWSxlQUFlLElBQUksTUFBTSxJQUFJO0FBQzdDLE1BQUksQ0FBQyxXQUFXO0FBQ1o7QUFBQSxFQUNKO0FBRUEsTUFBSSxhQUFhLElBQUksV0FBVyxNQUFNLE1BQU0sTUFBTSxJQUFJO0FBQ3RELE1BQUksWUFBWSxPQUFPO0FBQ25CLGVBQVcsU0FBUyxNQUFNO0FBQUEsRUFDOUI7QUFFQSxjQUFZLFVBQVUsT0FBTyxjQUFZLENBQUMsU0FBUyxTQUFTLFVBQVUsQ0FBQztBQUN2RSxNQUFJLFVBQVUsV0FBVyxHQUFHO0FBQ3hCLG1CQUFlLE9BQU8sTUFBTSxJQUFJO0FBQUEsRUFDcEMsT0FBTztBQUNILG1CQUFlLElBQUksTUFBTSxNQUFNLFNBQVM7QUFBQSxFQUM1QztBQUNKO0FBVU8sU0FBUyxXQUFXLFdBQW1CLFVBQW9CLGNBQXNCO0FBQ3BGLE1BQUksWUFBWSxlQUFlLElBQUksU0FBUyxLQUFLLENBQUM7QUFDbEQsUUFBTSxlQUFlLElBQUksU0FBUyxXQUFXLFVBQVUsWUFBWTtBQUNuRSxZQUFVLEtBQUssWUFBWTtBQUMzQixpQkFBZSxJQUFJLFdBQVcsU0FBUztBQUN2QyxTQUFPLE1BQU0sWUFBWSxZQUFZO0FBQ3pDO0FBU08sU0FBUyxHQUFHLFdBQW1CLFVBQWdDO0FBQ2xFLFNBQU8sV0FBVyxXQUFXLFVBQVUsRUFBRTtBQUM3QztBQVNPLFNBQVMsS0FBSyxXQUFtQixVQUFnQztBQUNwRSxTQUFPLFdBQVcsV0FBVyxVQUFVLENBQUM7QUFDNUM7QUFPTyxTQUFTLE9BQU8sWUFBeUM7QUFDNUQsYUFBVyxRQUFRLGVBQWEsZUFBZSxPQUFPLFNBQVMsQ0FBQztBQUNwRTtBQUtPLFNBQVMsU0FBZTtBQUMzQixpQkFBZSxNQUFNO0FBQ3pCO0FBU08sU0FBUyxLQUFLLE1BQWMsTUFBMkI7QUFDMUQsTUFBSTtBQUNKLE1BQUk7QUFFSixNQUFJLE9BQU8sU0FBUyxZQUFZLFNBQVMsUUFBUSxVQUFVLFFBQVEsVUFBVSxNQUFNO0FBRS9FLGdCQUFZLEtBQUssTUFBTTtBQUN2QixnQkFBWSxLQUFLLE1BQU07QUFBQSxFQUMzQixPQUFPO0FBRUgsZ0JBQVk7QUFDWixnQkFBWTtBQUFBLEVBQ2hCO0FBRUEsU0FBT0EsTUFBSyxZQUFZLEVBQUUsTUFBTSxXQUFXLE1BQU0sVUFBVSxDQUFDO0FBQ2hFOzs7QUdySU8sU0FBUyxTQUFTLFNBQWM7QUFFbkMsVUFBUTtBQUFBLElBQ0osa0JBQWtCLFVBQVU7QUFBQSxJQUM1QjtBQUFBLElBQ0E7QUFBQSxFQUNKO0FBQ0o7QUFNTyxTQUFTLGtCQUEyQjtBQUN2QyxTQUFRLElBQUksV0FBVyxXQUFXLEVBQUcsWUFBWTtBQUNyRDtBQU1PLFNBQVMsb0JBQW9CO0FBQ2hDLE1BQUksQ0FBQyxlQUFlLENBQUMsZUFBZSxDQUFDO0FBQ2pDLFdBQU87QUFFWCxNQUFJLFNBQVM7QUFFYixRQUFNLFNBQVMsSUFBSSxZQUFZO0FBQy9CLFFBQU0sYUFBYSxJQUFJLGdCQUFnQjtBQUN2QyxTQUFPLGlCQUFpQixRQUFRLE1BQU07QUFBRSxhQUFTO0FBQUEsRUFBTyxHQUFHLEVBQUUsUUFBUSxXQUFXLE9BQU8sQ0FBQztBQUN4RixhQUFXLE1BQU07QUFDakIsU0FBTyxjQUFjLElBQUksWUFBWSxNQUFNLENBQUM7QUFFNUMsU0FBTztBQUNYO0FBS08sU0FBUyxZQUFZLE9BQTJCO0FBdER2RCxNQUFBQztBQXVESSxNQUFJLE1BQU0sa0JBQWtCLGFBQWE7QUFDckMsV0FBTyxNQUFNO0FBQUEsRUFDakIsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLGdCQUFnQixNQUFNLGtCQUFrQixNQUFNO0FBQy9FLFlBQU9BLE1BQUEsTUFBTSxPQUFPLGtCQUFiLE9BQUFBLE1BQThCLFNBQVM7QUFBQSxFQUNsRCxPQUFPO0FBQ0gsV0FBTyxTQUFTO0FBQUEsRUFDcEI7QUFDSjtBQWlDQSxJQUFJLFVBQVU7QUFDZCxTQUFTLGlCQUFpQixvQkFBb0IsTUFBTTtBQUFFLFlBQVU7QUFBSyxDQUFDO0FBRS9ELFNBQVMsVUFBVSxVQUFzQjtBQUM1QyxNQUFJLFdBQVcsU0FBUyxlQUFlLFlBQVk7QUFDL0MsYUFBUztBQUFBLEVBQ2IsT0FBTztBQUNILGFBQVMsaUJBQWlCLG9CQUFvQixRQUFRO0FBQUEsRUFDMUQ7QUFDSjs7O0FDMUZBLElBQU0scUJBQXFCO0FBQzNCLElBQU0sdUJBQXVCO0FBQzdCLElBQUkseUJBQXlDO0FBRTdDLElBQU0saUJBQW9DO0FBQzFDLElBQU0sZUFBb0M7QUFDMUMsSUFBTSxjQUFvQztBQUMxQyxJQUFNLCtCQUFvQztBQUMxQyxJQUFNLDhCQUFvQztBQUMxQyxJQUFNLGNBQW9DO0FBQzFDLElBQU0sb0JBQW9DO0FBQzFDLElBQU0sbUJBQW9DO0FBQzFDLElBQU0sa0JBQW9DO0FBQzFDLElBQU0sZ0JBQW9DO0FBQzFDLElBQU0sZUFBb0M7QUFDMUMsSUFBTSxhQUFvQztBQUMxQyxJQUFNLGtCQUFvQztBQUMxQyxJQUFNLHFCQUFvQztBQUMxQyxJQUFNLG9CQUFvQztBQUMxQyxJQUFNLG9CQUFvQztBQUMxQyxJQUFNLGlCQUFvQztBQUMxQyxJQUFNLGlCQUFvQztBQUMxQyxJQUFNLGFBQW9DO0FBQzFDLElBQU0scUJBQW9DO0FBQzFDLElBQU0seUJBQW9DO0FBQzFDLElBQU0sZUFBb0M7QUFDMUMsSUFBTSxrQkFBb0M7QUFDMUMsSUFBTSxnQkFBb0M7QUFDMUMsSUFBTSxvQkFBb0M7QUFDMUMsSUFBTSx1QkFBb0M7QUFDMUMsSUFBTSw0QkFBb0M7QUFDMUMsSUFBTSxxQkFBb0M7QUFDMUMsSUFBTSxtQ0FBb0M7QUFDMUMsSUFBTSxtQkFBb0M7QUFDMUMsSUFBTSxtQkFBb0M7QUFDMUMsSUFBTSw0QkFBb0M7QUFDMUMsSUFBTSxxQkFBb0M7QUFDMUMsSUFBTSxnQkFBb0M7QUFDMUMsSUFBTSxpQkFBb0M7QUFDMUMsSUFBTSxnQkFBb0M7QUFDMUMsSUFBTSxhQUFvQztBQUMxQyxJQUFNLGFBQW9DO0FBQzFDLElBQU0seUJBQW9DO0FBQzFDLElBQU0sdUJBQW9DO0FBQzFDLElBQU0sd0JBQW9DO0FBQzFDLElBQU0scUJBQW9DO0FBQzFDLElBQU0sbUJBQW9DO0FBQzFDLElBQU0sbUJBQW9DO0FBQzFDLElBQU0sY0FBb0M7QUFDMUMsSUFBTSxhQUFvQztBQUMxQyxJQUFNLGVBQW9DO0FBQzFDLElBQU0sZ0JBQW9DO0FBQzFDLElBQU0sa0JBQW9DO0FBQzFDLElBQU0sd0JBQW9DO0FBRTFDLFNBQVMsbUJBQW1CLFNBQXlDO0FBQ2pFLE1BQUksQ0FBQyxTQUFTO0FBQ1YsV0FBTztBQUFBLEVBQ1g7QUFFQSxTQUFPLFFBQVEsUUFBUSxJQUFJLDJCQUFrQixJQUFHO0FBQ3BEO0FBdUJBLElBQU0sWUFBWSxPQUFPLFFBQVE7QUFJcEI7QUFGYixJQUFNLFVBQU4sTUFBTSxRQUFPO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFVVCxZQUFZLE9BQWUsSUFBSTtBQUMzQixTQUFLLFNBQVMsSUFBSSxpQkFBaUIsWUFBWSxRQUFRLElBQUk7QUFHM0QsZUFBVyxVQUFVLE9BQU8sb0JBQW9CLFFBQU8sU0FBUyxHQUFHO0FBQy9ELFVBQ0ksV0FBVyxpQkFDUixPQUFRLEtBQWEsTUFBTSxNQUFNLFlBQ3RDO0FBQ0UsUUFBQyxLQUFhLE1BQU0sSUFBSyxLQUFhLE1BQU0sRUFBRSxLQUFLLElBQUk7QUFBQSxNQUMzRDtBQUFBLElBQ0o7QUFBQSxFQUNKO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFRQSxJQUFJLE1BQXNCO0FBQ3RCLFdBQU8sSUFBSSxRQUFPLElBQUk7QUFBQSxFQUMxQjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9BLFdBQThCO0FBQzFCLFdBQU8sS0FBSyxTQUFTLEVBQUUsY0FBYztBQUFBLEVBQ3pDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxTQUF3QjtBQUNwQixXQUFPLEtBQUssU0FBUyxFQUFFLFlBQVk7QUFBQSxFQUN2QztBQUFBO0FBQUE7QUFBQTtBQUFBLEVBS0EsUUFBdUI7QUFDbkIsV0FBTyxLQUFLLFNBQVMsRUFBRSxXQUFXO0FBQUEsRUFDdEM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLHlCQUF3QztBQUNwQyxXQUFPLEtBQUssU0FBUyxFQUFFLDRCQUE0QjtBQUFBLEVBQ3ZEO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSx3QkFBdUM7QUFDbkMsV0FBTyxLQUFLLFNBQVMsRUFBRSwyQkFBMkI7QUFBQSxFQUN0RDtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBS0EsUUFBdUI7QUFDbkIsV0FBTyxLQUFLLFNBQVMsRUFBRSxXQUFXO0FBQUEsRUFDdEM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLGNBQTZCO0FBQ3pCLFdBQU8sS0FBSyxTQUFTLEVBQUUsaUJBQWlCO0FBQUEsRUFDNUM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLGFBQTRCO0FBQ3hCLFdBQU8sS0FBSyxTQUFTLEVBQUUsZ0JBQWdCO0FBQUEsRUFDM0M7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPQSxZQUE2QjtBQUN6QixXQUFPLEtBQUssU0FBUyxFQUFFLGVBQWU7QUFBQSxFQUMxQztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9BLFVBQTJCO0FBQ3ZCLFdBQU8sS0FBSyxTQUFTLEVBQUUsYUFBYTtBQUFBLEVBQ3hDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsU0FBMEI7QUFDdEIsV0FBTyxLQUFLLFNBQVMsRUFBRSxZQUFZO0FBQUEsRUFDdkM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLE9BQXNCO0FBQ2xCLFdBQU8sS0FBSyxTQUFTLEVBQUUsVUFBVTtBQUFBLEVBQ3JDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsWUFBOEI7QUFDMUIsV0FBTyxLQUFLLFNBQVMsRUFBRSxlQUFlO0FBQUEsRUFDMUM7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPQSxlQUFpQztBQUM3QixXQUFPLEtBQUssU0FBUyxFQUFFLGtCQUFrQjtBQUFBLEVBQzdDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsY0FBZ0M7QUFDNUIsV0FBTyxLQUFLLFNBQVMsRUFBRSxpQkFBaUI7QUFBQSxFQUM1QztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9BLGNBQWdDO0FBQzVCLFdBQU8sS0FBSyxTQUFTLEVBQUUsaUJBQWlCO0FBQUEsRUFDNUM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLFdBQTBCO0FBQ3RCLFdBQU8sS0FBSyxTQUFTLEVBQUUsY0FBYztBQUFBLEVBQ3pDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxXQUEwQjtBQUN0QixXQUFPLEtBQUssU0FBUyxFQUFFLGNBQWM7QUFBQSxFQUN6QztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9BLE9BQXdCO0FBQ3BCLFdBQU8sS0FBSyxTQUFTLEVBQUUsVUFBVTtBQUFBLEVBQ3JDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxlQUE4QjtBQUMxQixXQUFPLEtBQUssU0FBUyxFQUFFLGtCQUFrQjtBQUFBLEVBQzdDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsbUJBQXNDO0FBQ2xDLFdBQU8sS0FBSyxTQUFTLEVBQUUsc0JBQXNCO0FBQUEsRUFDakQ7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLFNBQXdCO0FBQ3BCLFdBQU8sS0FBSyxTQUFTLEVBQUUsWUFBWTtBQUFBLEVBQ3ZDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsWUFBOEI7QUFDMUIsV0FBTyxLQUFLLFNBQVMsRUFBRSxlQUFlO0FBQUEsRUFDMUM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLFVBQXlCO0FBQ3JCLFdBQU8sS0FBSyxTQUFTLEVBQUUsYUFBYTtBQUFBLEVBQ3hDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFRQSxZQUFZLEdBQVcsR0FBMEI7QUFDN0MsV0FBTyxLQUFLLFNBQVMsRUFBRSxtQkFBbUIsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUFBLEVBQ3REO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsZUFBZSxhQUFxQztBQUNoRCxXQUFPLEtBQUssU0FBUyxFQUFFLHNCQUFzQixFQUFFLFlBQVksQ0FBQztBQUFBLEVBQ2hFO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBVUEsb0JBQW9CLEdBQVcsR0FBVyxHQUFXLEdBQTBCO0FBQzNFLFdBQU8sS0FBSyxTQUFTLEVBQUUsMkJBQTJCLEVBQUUsR0FBRyxHQUFHLEdBQUcsRUFBRSxDQUFDO0FBQUEsRUFDcEU7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPQSxhQUFhLFdBQW1DO0FBQzVDLFdBQU8sS0FBSyxTQUFTLEVBQUUsb0JBQW9CLEVBQUUsVUFBVSxDQUFDO0FBQUEsRUFDNUQ7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPQSwyQkFBMkIsU0FBaUM7QUFDeEQsV0FBTyxLQUFLLFNBQVMsRUFBRSxrQ0FBa0MsRUFBRSxRQUFRLENBQUM7QUFBQSxFQUN4RTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsV0FBVyxPQUFlLFFBQStCO0FBQ3JELFdBQU8sS0FBSyxTQUFTLEVBQUUsa0JBQWtCLEVBQUUsT0FBTyxPQUFPLENBQUM7QUFBQSxFQUM5RDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsV0FBVyxPQUFlLFFBQStCO0FBQ3JELFdBQU8sS0FBSyxTQUFTLEVBQUUsa0JBQWtCLEVBQUUsT0FBTyxPQUFPLENBQUM7QUFBQSxFQUM5RDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsb0JBQW9CLEdBQVcsR0FBMEI7QUFDckQsV0FBTyxLQUFLLFNBQVMsRUFBRSwyQkFBMkIsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUFBLEVBQzlEO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsYUFBYUMsWUFBbUM7QUFDNUMsV0FBTyxLQUFLLFNBQVMsRUFBRSxvQkFBb0IsRUFBRSxXQUFBQSxXQUFVLENBQUM7QUFBQSxFQUM1RDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsUUFBUSxPQUFlLFFBQStCO0FBQ2xELFdBQU8sS0FBSyxTQUFTLEVBQUUsZUFBZSxFQUFFLE9BQU8sT0FBTyxDQUFDO0FBQUEsRUFDM0Q7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPQSxTQUFTLE9BQThCO0FBQ25DLFdBQU8sS0FBSyxTQUFTLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0FBQUEsRUFDcEQ7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPQSxRQUFRLE1BQTZCO0FBQ2pDLFdBQU8sS0FBSyxTQUFTLEVBQUUsZUFBZSxFQUFFLEtBQUssQ0FBQztBQUFBLEVBQ2xEO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxPQUFzQjtBQUNsQixXQUFPLEtBQUssU0FBUyxFQUFFLFVBQVU7QUFBQSxFQUNyQztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9BLE9BQXNCO0FBQ2xCLFdBQU8sS0FBSyxTQUFTLEVBQUUsVUFBVTtBQUFBLEVBQ3JDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxtQkFBa0M7QUFDOUIsV0FBTyxLQUFLLFNBQVMsRUFBRSxzQkFBc0I7QUFBQSxFQUNqRDtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBS0EsaUJBQWdDO0FBQzVCLFdBQU8sS0FBSyxTQUFTLEVBQUUsb0JBQW9CO0FBQUEsRUFDL0M7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLGtCQUFpQztBQUM3QixXQUFPLEtBQUssU0FBUyxFQUFFLHFCQUFxQjtBQUFBLEVBQ2hEO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxlQUE4QjtBQUMxQixXQUFPLEtBQUssU0FBUyxFQUFFLGtCQUFrQjtBQUFBLEVBQzdDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxhQUE0QjtBQUN4QixXQUFPLEtBQUssU0FBUyxFQUFFLGdCQUFnQjtBQUFBLEVBQzNDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxhQUE0QjtBQUN4QixXQUFPLEtBQUssU0FBUyxFQUFFLGdCQUFnQjtBQUFBLEVBQzNDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsUUFBeUI7QUFDckIsV0FBTyxLQUFLLFNBQVMsRUFBRSxXQUFXO0FBQUEsRUFDdEM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLE9BQXNCO0FBQ2xCLFdBQU8sS0FBSyxTQUFTLEVBQUUsVUFBVTtBQUFBLEVBQ3JDO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFLQSxTQUF3QjtBQUNwQixXQUFPLEtBQUssU0FBUyxFQUFFLFlBQVk7QUFBQSxFQUN2QztBQUFBO0FBQUE7QUFBQTtBQUFBLEVBS0EsVUFBeUI7QUFDckIsV0FBTyxLQUFLLFNBQVMsRUFBRSxhQUFhO0FBQUEsRUFDeEM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtBLFlBQTJCO0FBQ3ZCLFdBQU8sS0FBSyxTQUFTLEVBQUUsZUFBZTtBQUFBLEVBQzFDO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBVUEsdUJBQXVCLFdBQXFCLEdBQVcsR0FBaUI7QUFDcEUsVUFBTSxVQUFVLFNBQVMsaUJBQWlCLEdBQUcsQ0FBQztBQUc5QyxVQUFNLGlCQUFpQixtQkFBbUIsT0FBTztBQUVqRCxRQUFJLENBQUMsZ0JBQWdCO0FBQ2pCLGNBQVEsSUFBSSxxREFBcUQsVUFBQyxLQUFJLFVBQUMsNERBQTJELE9BQU87QUFFekk7QUFBQSxJQUNKO0FBRUEsWUFBUSxJQUFJLDJEQUEyRCxVQUFDLE1BQUssVUFBQyxPQUFNLFNBQVMsdUJBQXVCLGNBQWM7QUFDbEksVUFBTSxpQkFBaUI7QUFBQSxNQUNuQixJQUFJLGVBQWU7QUFBQSxNQUNuQixXQUFXLE1BQU0sS0FBSyxlQUFlLFNBQVM7QUFBQSxNQUM5QyxZQUFZLENBQUM7QUFBQSxJQUNqQjtBQUNBLGFBQVMsSUFBSSxHQUFHLElBQUksZUFBZSxXQUFXLFFBQVEsS0FBSztBQUN2RCxZQUFNLE9BQU8sZUFBZSxXQUFXLENBQUM7QUFDeEMscUJBQWUsV0FBVyxLQUFLLElBQUksSUFBSSxLQUFLO0FBQUEsSUFDaEQ7QUFFQSxVQUFNLFVBQVU7QUFBQSxNQUNaO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsSUFDSjtBQUVBLFNBQUssU0FBUyxFQUFFLHVCQUF1QixPQUFPO0FBQUEsRUFDbEQ7QUFDSjtBQTNkQSxJQUFNLFNBQU47QUFnZUEsSUFBTSxhQUFhLElBQUksT0FBTyxFQUFFO0FBR2hDLFNBQVMsK0JBQStCO0FBQ3BDLFFBQU0sYUFBYSxTQUFTO0FBQzVCLE1BQUksbUJBQW1CO0FBRXZCLGFBQVcsaUJBQWlCLGFBQWEsQ0FBQyxVQUFVO0FBQ2hELFVBQU0sZUFBZTtBQUNyQixRQUFJLE1BQU0sZ0JBQWdCLE1BQU0sYUFBYSxNQUFNLFNBQVMsT0FBTyxHQUFHO0FBQ2xFO0FBQ0EsWUFBTSxnQkFBZ0IsU0FBUyxpQkFBaUIsTUFBTSxTQUFTLE1BQU0sT0FBTztBQUM1RSxZQUFNLFdBQVcsbUJBQW1CLGFBQWE7QUFHakQsVUFBSSwwQkFBMEIsMkJBQTJCLFVBQVU7QUFDL0QsK0JBQXVCLFVBQVUsT0FBTyxvQkFBb0I7QUFBQSxNQUNoRTtBQUVBLFVBQUksVUFBVTtBQUNWLGlCQUFTLFVBQVUsSUFBSSxvQkFBb0I7QUFDM0MsY0FBTSxhQUFhLGFBQWE7QUFDaEMsaUNBQXlCO0FBQUEsTUFDN0IsT0FBTztBQUNILGNBQU0sYUFBYSxhQUFhO0FBQ2hDLGlDQUF5QjtBQUFBLE1BQzdCO0FBQUEsSUFDSjtBQUFBLEVBQ0osR0FBRyxLQUFLO0FBRVIsYUFBVyxpQkFBaUIsWUFBWSxDQUFDLFVBQVU7QUFDL0MsVUFBTSxlQUFlO0FBQ3JCLFFBQUksTUFBTSxnQkFBZ0IsTUFBTSxhQUFhLE1BQU0sU0FBUyxPQUFPLEdBQUc7QUFHbEUsVUFBSSx3QkFBd0I7QUFFeEIsWUFBRyxDQUFDLHVCQUF1QixVQUFVLFNBQVMsb0JBQW9CLEdBQUc7QUFDakUsaUNBQXVCLFVBQVUsSUFBSSxvQkFBb0I7QUFBQSxRQUM3RDtBQUNBLGNBQU0sYUFBYSxhQUFhO0FBQUEsTUFDcEMsT0FBTztBQUNILGNBQU0sYUFBYSxhQUFhO0FBQUEsTUFDcEM7QUFBQSxJQUNKO0FBQUEsRUFDSixHQUFHLEtBQUs7QUFFUixhQUFXLGlCQUFpQixhQUFhLENBQUMsVUFBVTtBQUNoRCxVQUFNLGVBQWU7QUFDckIsUUFBSSxNQUFNLGdCQUFnQixNQUFNLGFBQWEsTUFBTSxTQUFTLE9BQU8sR0FBRztBQUNsRTtBQUVBLFVBQUkscUJBQXFCLEtBQUssTUFBTSxrQkFBa0IsUUFBUywwQkFBMEIsQ0FBQyx1QkFBdUIsU0FBUyxNQUFNLGFBQXFCLEdBQUk7QUFDckosWUFBSSx3QkFBd0I7QUFDeEIsaUNBQXVCLFVBQVUsT0FBTyxvQkFBb0I7QUFDNUQsbUNBQXlCO0FBQUEsUUFDN0I7QUFDQSwyQkFBbUI7QUFBQSxNQUN2QjtBQUFBLElBQ0o7QUFBQSxFQUNKLEdBQUcsS0FBSztBQUVSLGFBQVcsaUJBQWlCLFFBQVEsQ0FBQyxVQUFVO0FBQzNDLFVBQU0sZUFBZTtBQUNyQix1QkFBbUI7QUFDbkIsUUFBSSx3QkFBd0I7QUFDeEIsNkJBQXVCLFVBQVUsT0FBTyxvQkFBb0I7QUFDNUQsK0JBQXlCO0FBQUEsSUFDN0I7QUFBQSxFQUdKLEdBQUcsS0FBSztBQUNaO0FBR0EsSUFBSSxPQUFPLFdBQVcsZUFBZSxPQUFPLGFBQWEsYUFBYTtBQUNsRSwrQkFBNkI7QUFDakM7QUFFQSxJQUFPLGlCQUFROzs7QVQ3bkJmLFNBQVMsVUFBVSxXQUFtQixPQUFZLE1BQVk7QUFDMUQsT0FBSyxJQUFJLFdBQVcsV0FBVyxJQUFJLENBQUM7QUFDeEM7QUFRQSxTQUFTLGlCQUFpQixZQUFvQixZQUFvQjtBQUM5RCxRQUFNLGVBQWUsZUFBTyxJQUFJLFVBQVU7QUFDMUMsUUFBTSxTQUFVLGFBQXFCLFVBQVU7QUFFL0MsTUFBSSxPQUFPLFdBQVcsWUFBWTtBQUM5QixZQUFRLE1BQU0sa0JBQWtCLG1CQUFVLGNBQWE7QUFDdkQ7QUFBQSxFQUNKO0FBRUEsTUFBSTtBQUNBLFdBQU8sS0FBSyxZQUFZO0FBQUEsRUFDNUIsU0FBUyxHQUFHO0FBQ1IsWUFBUSxNQUFNLGdDQUFnQyxtQkFBVSxRQUFPLENBQUM7QUFBQSxFQUNwRTtBQUNKO0FBS0EsU0FBUyxlQUFlLElBQWlCO0FBQ3JDLFFBQU0sVUFBVSxHQUFHO0FBRW5CLFdBQVMsVUFBVSxTQUFTLE9BQU87QUFDL0IsUUFBSSxXQUFXO0FBQ1g7QUFFSixVQUFNLFlBQVksUUFBUSxhQUFhLFdBQVcsS0FBSyxRQUFRLGFBQWEsZ0JBQWdCO0FBQzVGLFVBQU0sZUFBZSxRQUFRLGFBQWEsbUJBQW1CLEtBQUssUUFBUSxhQUFhLHdCQUF3QixLQUFLO0FBQ3BILFVBQU0sZUFBZSxRQUFRLGFBQWEsWUFBWSxLQUFLLFFBQVEsYUFBYSxpQkFBaUI7QUFDakcsVUFBTSxNQUFNLFFBQVEsYUFBYSxhQUFhLEtBQUssUUFBUSxhQUFhLGtCQUFrQjtBQUUxRixRQUFJLGNBQWM7QUFDZCxnQkFBVSxTQUFTO0FBQ3ZCLFFBQUksaUJBQWlCO0FBQ2pCLHVCQUFpQixjQUFjLFlBQVk7QUFDL0MsUUFBSSxRQUFRO0FBQ1IsV0FBSyxRQUFRLEdBQUc7QUFBQSxFQUN4QjtBQUVBLFFBQU0sVUFBVSxRQUFRLGFBQWEsYUFBYSxLQUFLLFFBQVEsYUFBYSxrQkFBa0I7QUFFOUYsTUFBSSxTQUFTO0FBQ1QsYUFBUztBQUFBLE1BQ0wsT0FBTztBQUFBLE1BQ1AsU0FBUztBQUFBLE1BQ1QsVUFBVTtBQUFBLE1BQ1YsU0FBUztBQUFBLFFBQ0wsRUFBRSxPQUFPLE1BQU07QUFBQSxRQUNmLEVBQUUsT0FBTyxNQUFNLFdBQVcsS0FBSztBQUFBLE1BQ25DO0FBQUEsSUFDSixDQUFDLEVBQUUsS0FBSyxTQUFTO0FBQUEsRUFDckIsT0FBTztBQUNILGNBQVU7QUFBQSxFQUNkO0FBQ0o7QUFHQSxJQUFNLGdCQUFnQixPQUFPLFlBQVk7QUFDekMsSUFBTSxnQkFBZ0IsT0FBTyxZQUFZO0FBQ3pDLElBQU0sa0JBQWtCLE9BQU8sY0FBYztBQVF4QztBQUZMLElBQU0sMEJBQU4sTUFBOEI7QUFBQSxFQUkxQixjQUFjO0FBQ1YsU0FBSyxhQUFhLElBQUksSUFBSSxnQkFBZ0I7QUFBQSxFQUM5QztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFTQSxJQUFJLFNBQWtCLFVBQTZDO0FBQy9ELFdBQU8sRUFBRSxRQUFRLEtBQUssYUFBYSxFQUFFLE9BQU87QUFBQSxFQUNoRDtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBS0EsUUFBYztBQUNWLFNBQUssYUFBYSxFQUFFLE1BQU07QUFDMUIsU0FBSyxhQUFhLElBQUksSUFBSSxnQkFBZ0I7QUFBQSxFQUM5QztBQUNKO0FBU0ssZUFFQTtBQUpMLElBQU0sa0JBQU4sTUFBc0I7QUFBQSxFQU1sQixjQUFjO0FBQ1YsU0FBSyxhQUFhLElBQUksb0JBQUksUUFBUTtBQUNsQyxTQUFLLGVBQWUsSUFBSTtBQUFBLEVBQzVCO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFRQSxJQUFJLFNBQWtCLFVBQTZDO0FBQy9ELFFBQUksQ0FBQyxLQUFLLGFBQWEsRUFBRSxJQUFJLE9BQU8sR0FBRztBQUFFLFdBQUssZUFBZTtBQUFBLElBQUs7QUFDbEUsU0FBSyxhQUFhLEVBQUUsSUFBSSxTQUFTLFFBQVE7QUFDekMsV0FBTyxDQUFDO0FBQUEsRUFDWjtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBS0EsUUFBYztBQUNWLFFBQUksS0FBSyxlQUFlLEtBQUs7QUFDekI7QUFFSixlQUFXLFdBQVcsU0FBUyxLQUFLLGlCQUFpQixHQUFHLEdBQUc7QUFDdkQsVUFBSSxLQUFLLGVBQWUsS0FBSztBQUN6QjtBQUVKLFlBQU0sV0FBVyxLQUFLLGFBQWEsRUFBRSxJQUFJLE9BQU87QUFDaEQsVUFBSSxZQUFZLE1BQU07QUFBRSxhQUFLLGVBQWU7QUFBQSxNQUFLO0FBRWpELGlCQUFXLFdBQVcsWUFBWSxDQUFDO0FBQy9CLGdCQUFRLG9CQUFvQixTQUFTLGNBQWM7QUFBQSxJQUMzRDtBQUVBLFNBQUssYUFBYSxJQUFJLG9CQUFJLFFBQVE7QUFDbEMsU0FBSyxlQUFlLElBQUk7QUFBQSxFQUM1QjtBQUNKO0FBRUEsSUFBTSxrQkFBa0Isa0JBQWtCLElBQUksSUFBSSx3QkFBd0IsSUFBSSxJQUFJLGdCQUFnQjtBQUtsRyxTQUFTLGdCQUFnQixTQUF3QjtBQUM3QyxRQUFNLGdCQUFnQjtBQUN0QixRQUFNLGNBQWUsUUFBUSxhQUFhLGFBQWEsS0FBSyxRQUFRLGFBQWEsa0JBQWtCLEtBQUs7QUFDeEcsUUFBTSxXQUFxQixDQUFDO0FBRTVCLE1BQUk7QUFDSixVQUFRLFFBQVEsY0FBYyxLQUFLLFdBQVcsT0FBTztBQUNqRCxhQUFTLEtBQUssTUFBTSxDQUFDLENBQUM7QUFFMUIsUUFBTSxVQUFVLGdCQUFnQixJQUFJLFNBQVMsUUFBUTtBQUNyRCxhQUFXLFdBQVc7QUFDbEIsWUFBUSxpQkFBaUIsU0FBUyxnQkFBZ0IsT0FBTztBQUNqRTtBQUtPLFNBQVMsU0FBZTtBQUMzQixZQUFVLE1BQU07QUFDcEI7QUFLTyxTQUFTLFNBQWU7QUFDM0Isa0JBQWdCLE1BQU07QUFDdEIsV0FBUyxLQUFLLGlCQUFpQixtR0FBbUcsRUFBRSxRQUFRLGVBQWU7QUFDL0o7OztBVWhNQSxPQUFPLFFBQVE7QUFDZixPQUFVO0FBRVYsSUFBSSxNQUFPO0FBQ1AsV0FBUyxzQkFBc0I7QUFDbkM7OztBQ3JCQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFZQSxJQUFNQyxRQUFPLGlCQUFpQixZQUFZLE1BQU07QUFFaEQsSUFBTSxtQkFBbUI7QUFDekIsSUFBTSxvQkFBb0I7QUFDMUIsSUFBTSxxQ0FBcUM7QUFFM0MsSUFBTSxVQUFXLFdBQVk7QUFsQjdCLE1BQUFDLEtBQUE7QUFtQkksTUFBSTtBQUNBLFNBQUssTUFBQUEsTUFBQSxPQUFlLFdBQWYsZ0JBQUFBLElBQXVCLFlBQXZCLG1CQUFnQyxhQUFhO0FBQzlDLGFBQVEsT0FBZSxPQUFPLFFBQVEsWUFBWSxLQUFNLE9BQWUsT0FBTyxPQUFPO0FBQUEsSUFDekYsWUFBWSx3QkFBZSxXQUFmLG1CQUF1QixvQkFBdkIsbUJBQXlDLGdCQUF6QyxtQkFBc0QsYUFBYTtBQUMzRSxhQUFRLE9BQWUsT0FBTyxnQkFBZ0IsVUFBVSxFQUFFLFlBQVksS0FBTSxPQUFlLE9BQU8sZ0JBQWdCLFVBQVUsQ0FBQztBQUFBLElBQ2pJO0FBQUEsRUFDSixTQUFRLEdBQUc7QUFBQSxFQUFDO0FBRVosVUFBUTtBQUFBLElBQUs7QUFBQSxJQUNUO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxFQUF3RDtBQUM1RCxTQUFPO0FBQ1gsRUFBRztBQUVJLFNBQVMsT0FBTyxLQUFnQjtBQUNuQyxxQ0FBVTtBQUNkO0FBT08sU0FBUyxhQUErQjtBQUMzQyxTQUFPRCxNQUFLLGdCQUFnQjtBQUNoQztBQU9BLGVBQXNCLGVBQTZDO0FBQy9ELE1BQUksV0FBVyxNQUFNLE1BQU0scUJBQXFCO0FBQ2hELE1BQUksU0FBUyxJQUFJO0FBQ2IsV0FBTyxTQUFTLEtBQUs7QUFBQSxFQUN6QixPQUFPO0FBQ0gsVUFBTSxJQUFJLE1BQU0sbUNBQW1DLFNBQVMsVUFBVTtBQUFBLEVBQzFFO0FBQ0o7QUErQk8sU0FBUyxjQUF3QztBQUNwRCxTQUFPQSxNQUFLLGlCQUFpQjtBQUNqQztBQU9PLFNBQVMsWUFBcUI7QUFDakMsU0FBTyxPQUFPLE9BQU8sWUFBWSxPQUFPO0FBQzVDO0FBT08sU0FBUyxVQUFtQjtBQUMvQixTQUFPLE9BQU8sT0FBTyxZQUFZLE9BQU87QUFDNUM7QUFPTyxTQUFTLFFBQWlCO0FBQzdCLFNBQU8sT0FBTyxPQUFPLFlBQVksT0FBTztBQUM1QztBQU9PLFNBQVMsVUFBbUI7QUFDL0IsU0FBTyxPQUFPLE9BQU8sWUFBWSxTQUFTO0FBQzlDO0FBT08sU0FBUyxRQUFpQjtBQUM3QixTQUFPLE9BQU8sT0FBTyxZQUFZLFNBQVM7QUFDOUM7QUFPTyxTQUFTLFVBQW1CO0FBQy9CLFNBQU8sT0FBTyxPQUFPLFlBQVksU0FBUztBQUM5QztBQU9PLFNBQVMsVUFBbUI7QUFDL0IsU0FBTyxRQUFRLE9BQU8sT0FBTyxZQUFZLEtBQUs7QUFDbEQ7QUFVTyxTQUFTLHVCQUF1QixXQUFxQixHQUFXLEdBQWlCO0FBQ3BGLFFBQU0sVUFBVSxTQUFTLGlCQUFpQixHQUFHLENBQUM7QUFDOUMsUUFBTSxZQUFZLFVBQVUsUUFBUSxLQUFLO0FBQ3pDLFFBQU0sWUFBWSxVQUFVLE1BQU0sS0FBSyxRQUFRLFNBQVMsSUFBSSxDQUFDO0FBRTdELFFBQU0sVUFBVTtBQUFBLElBQ1o7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsRUFDSjtBQUVBLEVBQUFBLE1BQUssb0NBQW9DLE9BQU8sRUFDM0MsS0FBSyxNQUFNO0FBRVIsWUFBUSxJQUFJLDhDQUE4QztBQUFBLEVBQzlELENBQUMsRUFDQSxNQUFNLFNBQU87QUFFVixZQUFRLE1BQU0sMkNBQTJDLEdBQUc7QUFBQSxFQUNoRSxDQUFDO0FBQ1Q7OztBQzVLQSxPQUFPLGlCQUFpQixlQUFlLGtCQUFrQjtBQUV6RCxJQUFNRSxRQUFPLGlCQUFpQixZQUFZLFdBQVc7QUFFckQsSUFBTSxrQkFBa0I7QUFFeEIsU0FBUyxnQkFBZ0IsSUFBWSxHQUFXLEdBQVcsTUFBaUI7QUFDeEUsT0FBS0EsTUFBSyxpQkFBaUIsRUFBQyxJQUFJLEdBQUcsR0FBRyxLQUFJLENBQUM7QUFDL0M7QUFFQSxTQUFTLG1CQUFtQixPQUFtQjtBQUMzQyxRQUFNLFNBQVMsWUFBWSxLQUFLO0FBR2hDLFFBQU0sb0JBQW9CLE9BQU8saUJBQWlCLE1BQU0sRUFBRSxpQkFBaUIsc0JBQXNCLEVBQUUsS0FBSztBQUV4RyxNQUFJLG1CQUFtQjtBQUNuQixVQUFNLGVBQWU7QUFDckIsVUFBTSxPQUFPLE9BQU8saUJBQWlCLE1BQU0sRUFBRSxpQkFBaUIsMkJBQTJCO0FBQ3pGLG9CQUFnQixtQkFBbUIsTUFBTSxTQUFTLE1BQU0sU0FBUyxJQUFJO0FBQUEsRUFDekUsT0FBTztBQUNILDhCQUEwQixPQUFPLE1BQU07QUFBQSxFQUMzQztBQUNKO0FBVUEsU0FBUywwQkFBMEIsT0FBbUIsUUFBcUI7QUFFdkUsTUFBSSxRQUFRLEdBQUc7QUFDWDtBQUFBLEVBQ0o7QUFHQSxVQUFRLE9BQU8saUJBQWlCLE1BQU0sRUFBRSxpQkFBaUIsdUJBQXVCLEVBQUUsS0FBSyxHQUFHO0FBQUEsSUFDdEYsS0FBSztBQUNEO0FBQUEsSUFDSixLQUFLO0FBQ0QsWUFBTSxlQUFlO0FBQ3JCO0FBQUEsRUFDUjtBQUdBLE1BQUksT0FBTyxtQkFBbUI7QUFDMUI7QUFBQSxFQUNKO0FBR0EsUUFBTSxZQUFZLE9BQU8sYUFBYTtBQUN0QyxRQUFNLGVBQWUsYUFBYSxVQUFVLFNBQVMsRUFBRSxTQUFTO0FBQ2hFLE1BQUksY0FBYztBQUNkLGFBQVMsSUFBSSxHQUFHLElBQUksVUFBVSxZQUFZLEtBQUs7QUFDM0MsWUFBTSxRQUFRLFVBQVUsV0FBVyxDQUFDO0FBQ3BDLFlBQU0sUUFBUSxNQUFNLGVBQWU7QUFDbkMsZUFBUyxJQUFJLEdBQUcsSUFBSSxNQUFNLFFBQVEsS0FBSztBQUNuQyxjQUFNLE9BQU8sTUFBTSxDQUFDO0FBQ3BCLFlBQUksU0FBUyxpQkFBaUIsS0FBSyxNQUFNLEtBQUssR0FBRyxNQUFNLFFBQVE7QUFDM0Q7QUFBQSxRQUNKO0FBQUEsTUFDSjtBQUFBLElBQ0o7QUFBQSxFQUNKO0FBR0EsTUFBSSxrQkFBa0Isb0JBQW9CLGtCQUFrQixxQkFBcUI7QUFDN0UsUUFBSSxnQkFBaUIsQ0FBQyxPQUFPLFlBQVksQ0FBQyxPQUFPLFVBQVc7QUFDeEQ7QUFBQSxJQUNKO0FBQUEsRUFDSjtBQUdBLFFBQU0sZUFBZTtBQUN6Qjs7O0FDN0ZBO0FBQUE7QUFBQTtBQUFBO0FBZ0JPLFNBQVMsUUFBUSxLQUFrQjtBQUN0QyxNQUFJO0FBQ0EsV0FBTyxPQUFPLE9BQU8sTUFBTSxHQUFHO0FBQUEsRUFDbEMsU0FBUyxHQUFHO0FBQ1IsVUFBTSxJQUFJLE1BQU0sOEJBQThCLE1BQU0sUUFBUSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUM7QUFBQSxFQUMvRTtBQUNKOzs7QUNQQSxJQUFJLFVBQVU7QUFDZCxJQUFJLFdBQVc7QUFFZixJQUFJLFlBQVk7QUFDaEIsSUFBSSxZQUFZO0FBQ2hCLElBQUksV0FBVztBQUNmLElBQUksYUFBcUI7QUFDekIsSUFBSSxnQkFBZ0I7QUFFcEIsSUFBSSxVQUFVO0FBQ2QsSUFBTSxpQkFBaUIsZ0JBQWdCO0FBRXZDLE9BQU8sU0FBUyxPQUFPLFVBQVUsQ0FBQztBQUNsQyxPQUFPLE9BQU8sZUFBZSxDQUFDLFVBQXlCO0FBQ25ELGNBQVk7QUFDWixNQUFJLENBQUMsV0FBVztBQUVaLGdCQUFZLFdBQVc7QUFDdkIsY0FBVTtBQUFBLEVBQ2Q7QUFDSjtBQUVBLE9BQU8saUJBQWlCLGFBQWEsUUFBUSxFQUFFLFNBQVMsS0FBSyxDQUFDO0FBQzlELE9BQU8saUJBQWlCLGFBQWEsUUFBUSxFQUFFLFNBQVMsS0FBSyxDQUFDO0FBQzlELE9BQU8saUJBQWlCLFdBQVcsUUFBUSxFQUFFLFNBQVMsS0FBSyxDQUFDO0FBQzVELFdBQVcsTUFBTSxDQUFDLFNBQVMsZUFBZSxVQUFVLEdBQUc7QUFDbkQsU0FBTyxpQkFBaUIsSUFBSSxlQUFlLEVBQUUsU0FBUyxLQUFLLENBQUM7QUFDaEU7QUFFQSxTQUFTLGNBQWMsT0FBYztBQUVqQyxNQUFJLFlBQVksVUFBVTtBQUN0QixVQUFNLHlCQUF5QjtBQUMvQixVQUFNLGdCQUFnQjtBQUN0QixVQUFNLGVBQWU7QUFBQSxFQUN6QjtBQUNKO0FBR0EsSUFBTSxZQUFZO0FBQ2xCLElBQU0sVUFBWTtBQUNsQixJQUFNLFlBQVk7QUFFbEIsU0FBUyxPQUFPLE9BQW1CO0FBSS9CLE1BQUksV0FBbUIsZUFBZSxNQUFNO0FBQzVDLFVBQVEsTUFBTSxNQUFNO0FBQUEsSUFDaEIsS0FBSztBQUNELGtCQUFZO0FBQ1osVUFBSSxDQUFDLGdCQUFnQjtBQUFFLHVCQUFlLFVBQVcsS0FBSyxNQUFNO0FBQUEsTUFBUztBQUNyRTtBQUFBLElBQ0osS0FBSztBQUNELGtCQUFZO0FBQ1osVUFBSSxDQUFDLGdCQUFnQjtBQUFFLHVCQUFlLFVBQVUsRUFBRSxLQUFLLE1BQU07QUFBQSxNQUFTO0FBQ3RFO0FBQUEsSUFDSjtBQUNJLGtCQUFZO0FBQ1osVUFBSSxDQUFDLGdCQUFnQjtBQUFFLHVCQUFlO0FBQUEsTUFBUztBQUMvQztBQUFBLEVBQ1I7QUFFQSxNQUFJLFdBQVcsVUFBVSxDQUFDO0FBQzFCLE1BQUksVUFBVSxlQUFlLENBQUM7QUFFOUIsWUFBVTtBQUdWLE1BQUksY0FBYyxhQUFhLEVBQUUsVUFBVSxNQUFNLFNBQVM7QUFDdEQsZ0JBQWEsS0FBSyxNQUFNO0FBQ3hCLGVBQVksS0FBSyxNQUFNO0FBQUEsRUFDM0I7QUFJQSxNQUNJLGNBQWMsYUFDWCxZQUVDLGFBRUksY0FBYyxhQUNYLE1BQU0sV0FBVyxJQUc5QjtBQUNFLFVBQU0seUJBQXlCO0FBQy9CLFVBQU0sZ0JBQWdCO0FBQ3RCLFVBQU0sZUFBZTtBQUFBLEVBQ3pCO0FBR0EsTUFBSSxXQUFXLEdBQUc7QUFBRSxjQUFVLEtBQUs7QUFBQSxFQUFHO0FBRXRDLE1BQUksVUFBVSxHQUFHO0FBQUUsZ0JBQVksS0FBSztBQUFBLEVBQUc7QUFHdkMsTUFBSSxjQUFjLFdBQVc7QUFBRSxnQkFBWSxLQUFLO0FBQUEsRUFBRztBQUFDO0FBQ3hEO0FBRUEsU0FBUyxZQUFZLE9BQXlCO0FBRTFDLFlBQVU7QUFDVixjQUFZO0FBR1osTUFBSSxDQUFDLFVBQVUsR0FBRztBQUNkLFFBQUksTUFBTSxTQUFTLGVBQWUsTUFBTSxXQUFXLEtBQUssTUFBTSxXQUFXLEdBQUc7QUFDeEU7QUFBQSxJQUNKO0FBQUEsRUFDSjtBQUVBLE1BQUksWUFBWTtBQUVaLGdCQUFZO0FBRVo7QUFBQSxFQUNKO0FBR0EsUUFBTSxTQUFTLFlBQVksS0FBSztBQUloQyxRQUFNLFFBQVEsT0FBTyxpQkFBaUIsTUFBTTtBQUM1QyxZQUNJLE1BQU0saUJBQWlCLG1CQUFtQixFQUFFLEtBQUssTUFBTSxXQUVuRCxNQUFNLFVBQVUsV0FBVyxNQUFNLFdBQVcsSUFBSSxPQUFPLGVBQ3BELE1BQU0sVUFBVSxXQUFXLE1BQU0sVUFBVSxJQUFJLE9BQU87QUFHckU7QUFFQSxTQUFTLFVBQVUsT0FBbUI7QUFFbEMsWUFBVTtBQUNWLGFBQVc7QUFDWCxjQUFZO0FBQ1osYUFBVztBQUNmO0FBRUEsSUFBTSxnQkFBZ0IsT0FBTyxPQUFPO0FBQUEsRUFDaEMsYUFBYTtBQUFBLEVBQ2IsYUFBYTtBQUFBLEVBQ2IsYUFBYTtBQUFBLEVBQ2IsYUFBYTtBQUFBLEVBQ2IsWUFBWTtBQUFBLEVBQ1osWUFBWTtBQUFBLEVBQ1osWUFBWTtBQUFBLEVBQ1osWUFBWTtBQUNoQixDQUFDO0FBRUQsU0FBUyxVQUFVLE1BQXlDO0FBQ3hELE1BQUksTUFBTTtBQUNOLFFBQUksQ0FBQyxZQUFZO0FBQUUsc0JBQWdCLFNBQVMsS0FBSyxNQUFNO0FBQUEsSUFBUTtBQUMvRCxhQUFTLEtBQUssTUFBTSxTQUFTLGNBQWMsSUFBSTtBQUFBLEVBQ25ELFdBQVcsQ0FBQyxRQUFRLFlBQVk7QUFDNUIsYUFBUyxLQUFLLE1BQU0sU0FBUztBQUFBLEVBQ2pDO0FBRUEsZUFBYSxRQUFRO0FBQ3pCO0FBRUEsU0FBUyxZQUFZLE9BQXlCO0FBQzFDLE1BQUksYUFBYSxZQUFZO0FBRXpCLGVBQVc7QUFDWCxXQUFPLGtCQUFrQixVQUFVO0FBQUEsRUFDdkMsV0FBVyxTQUFTO0FBRWhCLGVBQVc7QUFDWCxXQUFPLFlBQVk7QUFBQSxFQUN2QjtBQUVBLE1BQUksWUFBWSxVQUFVO0FBR3RCLGNBQVUsWUFBWTtBQUN0QjtBQUFBLEVBQ0o7QUFFQSxNQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsR0FBRztBQUM1QixRQUFJLFlBQVk7QUFBRSxnQkFBVTtBQUFBLElBQUc7QUFDL0I7QUFBQSxFQUNKO0FBRUEsUUFBTSxxQkFBcUIsUUFBUSwyQkFBMkIsS0FBSztBQUNuRSxRQUFNLG9CQUFvQixRQUFRLDBCQUEwQixLQUFLO0FBR2pFLFFBQU0sY0FBYyxRQUFRLG1CQUFtQixLQUFLO0FBRXBELFFBQU0sY0FBZSxPQUFPLGFBQWEsTUFBTSxVQUFXO0FBQzFELFFBQU0sYUFBYSxNQUFNLFVBQVU7QUFDbkMsUUFBTSxZQUFZLE1BQU0sVUFBVTtBQUNsQyxRQUFNLGVBQWdCLE9BQU8sY0FBYyxNQUFNLFVBQVc7QUFHNUQsUUFBTSxjQUFlLE9BQU8sYUFBYSxNQUFNLFVBQVksb0JBQW9CO0FBQy9FLFFBQU0sYUFBYSxNQUFNLFVBQVcsb0JBQW9CO0FBQ3hELFFBQU0sWUFBWSxNQUFNLFVBQVcscUJBQXFCO0FBQ3hELFFBQU0sZUFBZ0IsT0FBTyxjQUFjLE1BQU0sVUFBWSxxQkFBcUI7QUFFbEYsTUFBSSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLENBQUMsYUFBYTtBQUU1RCxjQUFVO0FBQUEsRUFDZCxXQUVTLGVBQWUsYUFBYyxXQUFVLFdBQVc7QUFBQSxXQUNsRCxjQUFjLGFBQWMsV0FBVSxXQUFXO0FBQUEsV0FDakQsY0FBYyxVQUFXLFdBQVUsV0FBVztBQUFBLFdBQzlDLGFBQWEsWUFBYSxXQUFVLFdBQVc7QUFBQSxXQUUvQyxXQUFZLFdBQVUsVUFBVTtBQUFBLFdBQ2hDLFVBQVcsV0FBVSxVQUFVO0FBQUEsV0FDL0IsYUFBYyxXQUFVLFVBQVU7QUFBQSxXQUNsQyxZQUFhLFdBQVUsVUFBVTtBQUFBLE1BRXJDLFdBQVU7QUFDbkI7OztBQzVPQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFXQSxJQUFNQyxRQUFPLGlCQUFpQixZQUFZLFdBQVc7QUFFckQsSUFBTUMsY0FBYTtBQUNuQixJQUFNQyxjQUFhO0FBQ25CLElBQU0sYUFBYTtBQUtaLFNBQVMsT0FBc0I7QUFDbEMsU0FBT0YsTUFBS0MsV0FBVTtBQUMxQjtBQUtPLFNBQVMsT0FBc0I7QUFDbEMsU0FBT0QsTUFBS0UsV0FBVTtBQUMxQjtBQUtPLFNBQVMsT0FBc0I7QUFDbEMsU0FBT0YsTUFBSyxVQUFVO0FBQzFCOzs7QUNwQ0E7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7OztBQ3dCQSxJQUFJLFVBQVUsU0FBUyxVQUFVO0FBQ2pDLElBQUksZUFBb0QsT0FBTyxZQUFZLFlBQVksWUFBWSxRQUFRLFFBQVE7QUFDbkgsSUFBSTtBQUNKLElBQUk7QUFDSixJQUFJLE9BQU8saUJBQWlCLGNBQWMsT0FBTyxPQUFPLG1CQUFtQixZQUFZO0FBQ25GLE1BQUk7QUFDQSxtQkFBZSxPQUFPLGVBQWUsQ0FBQyxHQUFHLFVBQVU7QUFBQSxNQUMvQyxLQUFLLFdBQVk7QUFDYixjQUFNO0FBQUEsTUFDVjtBQUFBLElBQ0osQ0FBQztBQUNELHVCQUFtQixDQUFDO0FBRXBCLGlCQUFhLFdBQVk7QUFBRSxZQUFNO0FBQUEsSUFBSSxHQUFHLE1BQU0sWUFBWTtBQUFBLEVBQzlELFNBQVMsR0FBRztBQUNSLFFBQUksTUFBTSxrQkFBa0I7QUFDeEIscUJBQWU7QUFBQSxJQUNuQjtBQUFBLEVBQ0o7QUFDSixPQUFPO0FBQ0gsaUJBQWU7QUFDbkI7QUFFQSxJQUFJLG1CQUFtQjtBQUN2QixJQUFJLGVBQWUsU0FBUyxtQkFBbUIsT0FBcUI7QUFDaEUsTUFBSTtBQUNBLFFBQUksUUFBUSxRQUFRLEtBQUssS0FBSztBQUM5QixXQUFPLGlCQUFpQixLQUFLLEtBQUs7QUFBQSxFQUN0QyxTQUFTLEdBQUc7QUFDUixXQUFPO0FBQUEsRUFDWDtBQUNKO0FBRUEsSUFBSSxvQkFBb0IsU0FBUyxpQkFBaUIsT0FBcUI7QUFDbkUsTUFBSTtBQUNBLFFBQUksYUFBYSxLQUFLLEdBQUc7QUFBRSxhQUFPO0FBQUEsSUFBTztBQUN6QyxZQUFRLEtBQUssS0FBSztBQUNsQixXQUFPO0FBQUEsRUFDWCxTQUFTLEdBQUc7QUFDUixXQUFPO0FBQUEsRUFDWDtBQUNKO0FBQ0EsSUFBSSxRQUFRLE9BQU8sVUFBVTtBQUM3QixJQUFJLGNBQWM7QUFDbEIsSUFBSSxVQUFVO0FBQ2QsSUFBSSxXQUFXO0FBQ2YsSUFBSSxXQUFXO0FBQ2YsSUFBSSxZQUFZO0FBQ2hCLElBQUksWUFBWTtBQUNoQixJQUFJLGlCQUFpQixPQUFPLFdBQVcsY0FBYyxDQUFDLENBQUMsT0FBTztBQUU5RCxJQUFJLFNBQVMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUV0QixJQUFJLFFBQWlDLFNBQVMsbUJBQW1CO0FBQUUsU0FBTztBQUFPO0FBQ2pGLElBQUksT0FBTyxhQUFhLFVBQVU7QUFFMUIsUUFBTSxTQUFTO0FBQ25CLE1BQUksTUFBTSxLQUFLLEdBQUcsTUFBTSxNQUFNLEtBQUssU0FBUyxHQUFHLEdBQUc7QUFDOUMsWUFBUSxTQUFTRyxrQkFBaUIsT0FBTztBQUdyQyxXQUFLLFVBQVUsQ0FBQyxXQUFXLE9BQU8sVUFBVSxlQUFlLE9BQU8sVUFBVSxXQUFXO0FBQ25GLFlBQUk7QUFDQSxjQUFJLE1BQU0sTUFBTSxLQUFLLEtBQUs7QUFDMUIsa0JBQ0ksUUFBUSxZQUNMLFFBQVEsYUFDUixRQUFRLGFBQ1IsUUFBUSxnQkFDVixNQUFNLEVBQUUsS0FBSztBQUFBLFFBQ3RCLFNBQVMsR0FBRztBQUFBLFFBQU87QUFBQSxNQUN2QjtBQUNBLGFBQU87QUFBQSxJQUNYO0FBQUEsRUFDSjtBQUNKO0FBbkJRO0FBcUJSLFNBQVMsbUJBQXNCLE9BQXVEO0FBQ2xGLE1BQUksTUFBTSxLQUFLLEdBQUc7QUFBRSxXQUFPO0FBQUEsRUFBTTtBQUNqQyxNQUFJLENBQUMsT0FBTztBQUFFLFdBQU87QUFBQSxFQUFPO0FBQzVCLE1BQUksT0FBTyxVQUFVLGNBQWMsT0FBTyxVQUFVLFVBQVU7QUFBRSxXQUFPO0FBQUEsRUFBTztBQUM5RSxNQUFJO0FBQ0EsSUFBQyxhQUFxQixPQUFPLE1BQU0sWUFBWTtBQUFBLEVBQ25ELFNBQVMsR0FBRztBQUNSLFFBQUksTUFBTSxrQkFBa0I7QUFBRSxhQUFPO0FBQUEsSUFBTztBQUFBLEVBQ2hEO0FBQ0EsU0FBTyxDQUFDLGFBQWEsS0FBSyxLQUFLLGtCQUFrQixLQUFLO0FBQzFEO0FBRUEsU0FBUyxxQkFBd0IsT0FBc0Q7QUFDbkYsTUFBSSxNQUFNLEtBQUssR0FBRztBQUFFLFdBQU87QUFBQSxFQUFNO0FBQ2pDLE1BQUksQ0FBQyxPQUFPO0FBQUUsV0FBTztBQUFBLEVBQU87QUFDNUIsTUFBSSxPQUFPLFVBQVUsY0FBYyxPQUFPLFVBQVUsVUFBVTtBQUFFLFdBQU87QUFBQSxFQUFPO0FBQzlFLE1BQUksZ0JBQWdCO0FBQUUsV0FBTyxrQkFBa0IsS0FBSztBQUFBLEVBQUc7QUFDdkQsTUFBSSxhQUFhLEtBQUssR0FBRztBQUFFLFdBQU87QUFBQSxFQUFPO0FBQ3pDLE1BQUksV0FBVyxNQUFNLEtBQUssS0FBSztBQUMvQixNQUFJLGFBQWEsV0FBVyxhQUFhLFlBQVksQ0FBRSxpQkFBa0IsS0FBSyxRQUFRLEdBQUc7QUFBRSxXQUFPO0FBQUEsRUFBTztBQUN6RyxTQUFPLGtCQUFrQixLQUFLO0FBQ2xDO0FBRUEsSUFBTyxtQkFBUSxlQUFlLHFCQUFxQjs7O0FDekc1QyxJQUFNLGNBQU4sY0FBMEIsTUFBTTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU1uQyxZQUFZLFNBQWtCLFNBQXdCO0FBQ2xELFVBQU0sU0FBUyxPQUFPO0FBQ3RCLFNBQUssT0FBTztBQUFBLEVBQ2hCO0FBQ0o7QUFjTyxJQUFNLDBCQUFOLGNBQXNDLE1BQU07QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBYS9DLFlBQVksU0FBc0MsUUFBYyxNQUFlO0FBQzNFLFdBQU8sc0JBQVEsK0NBQStDLGNBQWMsYUFBYSxNQUFNLEdBQUcsRUFBRSxPQUFPLE9BQU8sQ0FBQztBQUNuSCxTQUFLLFVBQVU7QUFDZixTQUFLLE9BQU87QUFBQSxFQUNoQjtBQUNKO0FBK0JBLElBQU0sYUFBYSxPQUFPLFNBQVM7QUFDbkMsSUFBTSxnQkFBZ0IsT0FBTyxZQUFZO0FBN0Z6QztBQThGQSxJQUFNLFdBQVUsWUFBTyxZQUFQLFlBQWtCLE9BQU8saUJBQWlCO0FBb0RuRCxJQUFNLHFCQUFOLE1BQU0sNEJBQThCLFFBQWdFO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBdUN2RyxZQUFZLFVBQXlDLGFBQTJDO0FBQzVGLFFBQUk7QUFDSixRQUFJO0FBQ0osVUFBTSxDQUFDLEtBQUssUUFBUTtBQUFFLGdCQUFVO0FBQUssZUFBUztBQUFBLElBQUssQ0FBQztBQUVwRCxRQUFLLEtBQUssWUFBb0IsT0FBTyxNQUFNLFNBQVM7QUFDaEQsWUFBTSxJQUFJLFVBQVUsbUlBQW1JO0FBQUEsSUFDM0o7QUFFQSxRQUFJLFVBQThDO0FBQUEsTUFDOUMsU0FBUztBQUFBLE1BQ1Q7QUFBQSxNQUNBO0FBQUEsTUFDQSxJQUFJLGNBQWM7QUFBRSxlQUFPLG9DQUFlO0FBQUEsTUFBTTtBQUFBLE1BQ2hELElBQUksWUFBWSxJQUFJO0FBQUUsc0JBQWMsa0JBQU07QUFBQSxNQUFXO0FBQUEsSUFDekQ7QUFFQSxVQUFNLFFBQWlDO0FBQUEsTUFDbkMsSUFBSSxPQUFPO0FBQUUsZUFBTztBQUFBLE1BQU87QUFBQSxNQUMzQixXQUFXO0FBQUEsTUFDWCxTQUFTO0FBQUEsSUFDYjtBQUdBLFNBQUssT0FBTyxpQkFBaUIsTUFBTTtBQUFBLE1BQy9CLENBQUMsVUFBVSxHQUFHO0FBQUEsUUFDVixjQUFjO0FBQUEsUUFDZCxZQUFZO0FBQUEsUUFDWixVQUFVO0FBQUEsUUFDVixPQUFPO0FBQUEsTUFDWDtBQUFBLE1BQ0EsQ0FBQyxhQUFhLEdBQUc7QUFBQSxRQUNiLGNBQWM7QUFBQSxRQUNkLFlBQVk7QUFBQSxRQUNaLFVBQVU7QUFBQSxRQUNWLE9BQU8sYUFBYSxTQUFTLEtBQUs7QUFBQSxNQUN0QztBQUFBLElBQ0osQ0FBQztBQUdELFVBQU0sV0FBVyxZQUFZLFNBQVMsS0FBSztBQUMzQyxRQUFJO0FBQ0EsZUFBUyxZQUFZLFNBQVMsS0FBSyxHQUFHLFFBQVE7QUFBQSxJQUNsRCxTQUFTLEtBQUs7QUFDVixVQUFJLE1BQU0sV0FBVztBQUNqQixnQkFBUSxJQUFJLHVEQUF1RCxHQUFHO0FBQUEsTUFDMUUsT0FBTztBQUNILGlCQUFTLEdBQUc7QUFBQSxNQUNoQjtBQUFBLElBQ0o7QUFBQSxFQUNKO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQXlEQSxPQUFPLE9BQXVDO0FBQzFDLFdBQU8sSUFBSSxvQkFBeUIsQ0FBQyxZQUFZO0FBRzdDLGNBQVEsSUFBSTtBQUFBLFFBQ1IsS0FBSyxhQUFhLEVBQUUsSUFBSSxZQUFZLHNCQUFzQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQUEsUUFDcEUsZUFBZSxJQUFJO0FBQUEsTUFDdkIsQ0FBQyxFQUFFLEtBQUssTUFBTSxRQUFRLEdBQUcsTUFBTSxRQUFRLENBQUM7QUFBQSxJQUM1QyxDQUFDO0FBQUEsRUFDTDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUEyQkEsU0FBUyxRQUE0QztBQUNqRCxRQUFJLE9BQU8sU0FBUztBQUNoQixXQUFLLEtBQUssT0FBTyxPQUFPLE1BQU07QUFBQSxJQUNsQyxPQUFPO0FBQ0gsYUFBTyxpQkFBaUIsU0FBUyxNQUFNLEtBQUssS0FBSyxPQUFPLE9BQU8sTUFBTSxHQUFHLEVBQUMsU0FBUyxLQUFJLENBQUM7QUFBQSxJQUMzRjtBQUVBLFdBQU87QUFBQSxFQUNYO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBK0JBLEtBQXFDLGFBQXNILFlBQXdILGFBQW9GO0FBQ25XLFFBQUksRUFBRSxnQkFBZ0Isc0JBQXFCO0FBQ3ZDLFlBQU0sSUFBSSxVQUFVLGdFQUFnRTtBQUFBLElBQ3hGO0FBTUEsUUFBSSxDQUFDLGlCQUFXLFdBQVcsR0FBRztBQUFFLG9CQUFjO0FBQUEsSUFBaUI7QUFDL0QsUUFBSSxDQUFDLGlCQUFXLFVBQVUsR0FBRztBQUFFLG1CQUFhO0FBQUEsSUFBUztBQUVyRCxRQUFJLGdCQUFnQixZQUFZLGNBQWMsU0FBUztBQUVuRCxhQUFPLElBQUksb0JBQW1CLENBQUMsWUFBWSxRQUFRLElBQVcsQ0FBQztBQUFBLElBQ25FO0FBRUEsVUFBTSxVQUErQyxDQUFDO0FBQ3RELFNBQUssVUFBVSxJQUFJO0FBRW5CLFdBQU8sSUFBSSxvQkFBd0MsQ0FBQyxTQUFTLFdBQVc7QUFDcEUsV0FBSyxNQUFNO0FBQUEsUUFDUCxDQUFDLFVBQVU7QUFyWTNCLGNBQUFDO0FBc1lvQixjQUFJLEtBQUssVUFBVSxNQUFNLFNBQVM7QUFBRSxpQkFBSyxVQUFVLElBQUk7QUFBQSxVQUFNO0FBQzdELFdBQUFBLE1BQUEsUUFBUSxZQUFSLGdCQUFBQSxJQUFBO0FBRUEsY0FBSTtBQUNBLG9CQUFRLFlBQWEsS0FBSyxDQUFDO0FBQUEsVUFDL0IsU0FBUyxLQUFLO0FBQ1YsbUJBQU8sR0FBRztBQUFBLFVBQ2Q7QUFBQSxRQUNKO0FBQUEsUUFDQSxDQUFDLFdBQVk7QUEvWTdCLGNBQUFBO0FBZ1pvQixjQUFJLEtBQUssVUFBVSxNQUFNLFNBQVM7QUFBRSxpQkFBSyxVQUFVLElBQUk7QUFBQSxVQUFNO0FBQzdELFdBQUFBLE1BQUEsUUFBUSxZQUFSLGdCQUFBQSxJQUFBO0FBRUEsY0FBSTtBQUNBLG9CQUFRLFdBQVksTUFBTSxDQUFDO0FBQUEsVUFDL0IsU0FBUyxLQUFLO0FBQ1YsbUJBQU8sR0FBRztBQUFBLFVBQ2Q7QUFBQSxRQUNKO0FBQUEsTUFDSjtBQUFBLElBQ0osR0FBRyxPQUFPLFVBQVc7QUFFakIsVUFBSTtBQUNBLGVBQU8sMkNBQWM7QUFBQSxNQUN6QixVQUFFO0FBQ0UsY0FBTSxLQUFLLE9BQU8sS0FBSztBQUFBLE1BQzNCO0FBQUEsSUFDSixDQUFDO0FBQUEsRUFDTDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQStCQSxNQUF1QixZQUFxRixhQUE0RTtBQUNwTCxXQUFPLEtBQUssS0FBSyxRQUFXLFlBQVksV0FBVztBQUFBLEVBQ3ZEO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQWlDQSxRQUFRLFdBQTZDLGFBQWtFO0FBQ25ILFFBQUksRUFBRSxnQkFBZ0Isc0JBQXFCO0FBQ3ZDLFlBQU0sSUFBSSxVQUFVLG1FQUFtRTtBQUFBLElBQzNGO0FBRUEsUUFBSSxDQUFDLGlCQUFXLFNBQVMsR0FBRztBQUN4QixhQUFPLEtBQUssS0FBSyxXQUFXLFdBQVcsV0FBVztBQUFBLElBQ3REO0FBRUEsV0FBTyxLQUFLO0FBQUEsTUFDUixDQUFDLFVBQVUsb0JBQW1CLFFBQVEsVUFBVSxDQUFDLEVBQUUsS0FBSyxNQUFNLEtBQUs7QUFBQSxNQUNuRSxDQUFDLFdBQVksb0JBQW1CLFFBQVEsVUFBVSxDQUFDLEVBQUUsS0FBSyxNQUFNO0FBQUUsY0FBTTtBQUFBLE1BQVEsQ0FBQztBQUFBLE1BQ2pGO0FBQUEsSUFDSjtBQUFBLEVBQ0o7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBWUEsYUF6V1MsWUFFUyxlQXVXTixRQUFPLElBQUk7QUFDbkIsV0FBTztBQUFBLEVBQ1g7QUFBQSxFQWFBLE9BQU8sSUFBc0QsUUFBd0M7QUFDakcsUUFBSSxZQUFZLE1BQU0sS0FBSyxNQUFNO0FBQ2pDLFVBQU0sVUFBVSxVQUFVLFdBQVcsSUFDL0Isb0JBQW1CLFFBQVEsU0FBUyxJQUNwQyxJQUFJLG9CQUE0QixDQUFDLFNBQVMsV0FBVztBQUNuRCxXQUFLLFFBQVEsSUFBSSxTQUFTLEVBQUUsS0FBSyxTQUFTLE1BQU07QUFBQSxJQUNwRCxHQUFHLENBQUMsVUFBMEIsVUFBVSxTQUFTLFdBQVcsS0FBSyxDQUFDO0FBQ3RFLFdBQU87QUFBQSxFQUNYO0FBQUEsRUFhQSxPQUFPLFdBQTZELFFBQXdDO0FBQ3hHLFFBQUksWUFBWSxNQUFNLEtBQUssTUFBTTtBQUNqQyxVQUFNLFVBQVUsVUFBVSxXQUFXLElBQy9CLG9CQUFtQixRQUFRLFNBQVMsSUFDcEMsSUFBSSxvQkFBNEIsQ0FBQyxTQUFTLFdBQVc7QUFDbkQsV0FBSyxRQUFRLFdBQVcsU0FBUyxFQUFFLEtBQUssU0FBUyxNQUFNO0FBQUEsSUFDM0QsR0FBRyxDQUFDLFVBQTBCLFVBQVUsU0FBUyxXQUFXLEtBQUssQ0FBQztBQUN0RSxXQUFPO0FBQUEsRUFDWDtBQUFBLEVBZUEsT0FBTyxJQUFzRCxRQUF3QztBQUNqRyxRQUFJLFlBQVksTUFBTSxLQUFLLE1BQU07QUFDakMsVUFBTSxVQUFVLFVBQVUsV0FBVyxJQUMvQixvQkFBbUIsUUFBUSxTQUFTLElBQ3BDLElBQUksb0JBQTRCLENBQUMsU0FBUyxXQUFXO0FBQ25ELFdBQUssUUFBUSxJQUFJLFNBQVMsRUFBRSxLQUFLLFNBQVMsTUFBTTtBQUFBLElBQ3BELEdBQUcsQ0FBQyxVQUEwQixVQUFVLFNBQVMsV0FBVyxLQUFLLENBQUM7QUFDdEUsV0FBTztBQUFBLEVBQ1g7QUFBQSxFQVlBLE9BQU8sS0FBdUQsUUFBd0M7QUFDbEcsUUFBSSxZQUFZLE1BQU0sS0FBSyxNQUFNO0FBQ2pDLFVBQU0sVUFBVSxJQUFJLG9CQUE0QixDQUFDLFNBQVMsV0FBVztBQUNqRSxXQUFLLFFBQVEsS0FBSyxTQUFTLEVBQUUsS0FBSyxTQUFTLE1BQU07QUFBQSxJQUNyRCxHQUFHLENBQUMsVUFBMEIsVUFBVSxTQUFTLFdBQVcsS0FBSyxDQUFDO0FBQ2xFLFdBQU87QUFBQSxFQUNYO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsT0FBTyxPQUFrQixPQUFvQztBQUN6RCxVQUFNLElBQUksSUFBSSxvQkFBc0IsTUFBTTtBQUFBLElBQUMsQ0FBQztBQUM1QyxNQUFFLE9BQU8sS0FBSztBQUNkLFdBQU87QUFBQSxFQUNYO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQVlBLE9BQU8sUUFBbUIsY0FBc0IsT0FBb0M7QUFDaEYsVUFBTSxVQUFVLElBQUksb0JBQXNCLE1BQU07QUFBQSxJQUFDLENBQUM7QUFDbEQsUUFBSSxlQUFlLE9BQU8sZ0JBQWdCLGNBQWMsWUFBWSxXQUFXLE9BQU8sWUFBWSxZQUFZLFlBQVk7QUFDdEgsa0JBQVksUUFBUSxZQUFZLEVBQUUsaUJBQWlCLFNBQVMsTUFBTSxLQUFLLFFBQVEsT0FBTyxLQUFLLENBQUM7QUFBQSxJQUNoRyxPQUFPO0FBQ0gsaUJBQVcsTUFBTSxLQUFLLFFBQVEsT0FBTyxLQUFLLEdBQUcsWUFBWTtBQUFBLElBQzdEO0FBQ0EsV0FBTztBQUFBLEVBQ1g7QUFBQSxFQWlCQSxPQUFPLE1BQWdCLGNBQXNCLE9BQWtDO0FBQzNFLFdBQU8sSUFBSSxvQkFBc0IsQ0FBQyxZQUFZO0FBQzFDLGlCQUFXLE1BQU0sUUFBUSxLQUFNLEdBQUcsWUFBWTtBQUFBLElBQ2xELENBQUM7QUFBQSxFQUNMO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsT0FBTyxPQUFrQixRQUFxQztBQUMxRCxXQUFPLElBQUksb0JBQXNCLENBQUMsR0FBRyxXQUFXLE9BQU8sTUFBTSxDQUFDO0FBQUEsRUFDbEU7QUFBQSxFQW9CQSxPQUFPLFFBQWtCLE9BQTREO0FBQ2pGLFFBQUksaUJBQWlCLHFCQUFvQjtBQUVyQyxhQUFPO0FBQUEsSUFDWDtBQUNBLFdBQU8sSUFBSSxvQkFBd0IsQ0FBQyxZQUFZLFFBQVEsS0FBSyxDQUFDO0FBQUEsRUFDbEU7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFVQSxPQUFPLGdCQUF1RDtBQUMxRCxRQUFJLFNBQTZDLEVBQUUsYUFBYSxLQUFLO0FBQ3JFLFdBQU8sVUFBVSxJQUFJLG9CQUFzQixDQUFDLFNBQVMsV0FBVztBQUM1RCxhQUFPLFVBQVU7QUFDakIsYUFBTyxTQUFTO0FBQUEsSUFDcEIsR0FBRyxDQUFDLFVBQWdCO0FBenJCNUIsVUFBQUE7QUF5ckI4QixPQUFBQSxNQUFBLE9BQU8sZ0JBQVAsZ0JBQUFBLElBQUEsYUFBcUI7QUFBQSxJQUFRLENBQUM7QUFDcEQsV0FBTztBQUFBLEVBQ1g7QUFDSjtBQU1BLFNBQVMsYUFBZ0IsU0FBNkMsT0FBZ0M7QUFDbEcsTUFBSSxzQkFBZ0Q7QUFFcEQsU0FBTyxDQUFDLFdBQWtEO0FBQ3RELFFBQUksQ0FBQyxNQUFNLFNBQVM7QUFDaEIsWUFBTSxVQUFVO0FBQ2hCLFlBQU0sU0FBUztBQUNmLGNBQVEsT0FBTyxNQUFNO0FBTXJCLFdBQUssUUFBUSxVQUFVLEtBQUssS0FBSyxRQUFRLFNBQVMsUUFBVyxDQUFDLFFBQVE7QUFDbEUsWUFBSSxRQUFRLFFBQVE7QUFDaEIsZ0JBQU07QUFBQSxRQUNWO0FBQUEsTUFDSixDQUFDO0FBQUEsSUFDTDtBQUlBLFFBQUksQ0FBQyxNQUFNLFVBQVUsQ0FBQyxRQUFRLGFBQWE7QUFBRTtBQUFBLElBQVE7QUFFckQsMEJBQXNCLElBQUksUUFBYyxDQUFDLFlBQVk7QUFDakQsVUFBSTtBQUNBLGdCQUFRLFFBQVEsWUFBYSxNQUFNLE9BQVEsS0FBSyxDQUFDO0FBQUEsTUFDckQsU0FBUyxLQUFLO0FBQ1YsZ0JBQVEsT0FBTyxJQUFJLHdCQUF3QixRQUFRLFNBQVMsS0FBSyw4Q0FBOEMsQ0FBQztBQUFBLE1BQ3BIO0FBQUEsSUFDSixDQUFDLEVBQUUsTUFBTSxDQUFDQyxZQUFZO0FBQ2xCLGNBQVEsT0FBTyxJQUFJLHdCQUF3QixRQUFRLFNBQVNBLFNBQVEsOENBQThDLENBQUM7QUFBQSxJQUN2SCxDQUFDO0FBR0QsWUFBUSxjQUFjO0FBRXRCLFdBQU87QUFBQSxFQUNYO0FBQ0o7QUFLQSxTQUFTLFlBQWUsU0FBNkMsT0FBK0Q7QUFDaEksU0FBTyxDQUFDLFVBQVU7QUFDZCxRQUFJLE1BQU0sV0FBVztBQUFFO0FBQUEsSUFBUTtBQUMvQixVQUFNLFlBQVk7QUFFbEIsUUFBSSxVQUFVLFFBQVEsU0FBUztBQUMzQixVQUFJLE1BQU0sU0FBUztBQUFFO0FBQUEsTUFBUTtBQUM3QixZQUFNLFVBQVU7QUFDaEIsY0FBUSxPQUFPLElBQUksVUFBVSwyQ0FBMkMsQ0FBQztBQUN6RTtBQUFBLElBQ0o7QUFFQSxRQUFJLFNBQVMsU0FBUyxPQUFPLFVBQVUsWUFBWSxPQUFPLFVBQVUsYUFBYTtBQUM3RSxVQUFJO0FBQ0osVUFBSTtBQUNBLGVBQVEsTUFBYztBQUFBLE1BQzFCLFNBQVMsS0FBSztBQUNWLGNBQU0sVUFBVTtBQUNoQixnQkFBUSxPQUFPLEdBQUc7QUFDbEI7QUFBQSxNQUNKO0FBRUEsVUFBSSxpQkFBVyxJQUFJLEdBQUc7QUFDbEIsWUFBSTtBQUNBLGNBQUksU0FBVSxNQUFjO0FBQzVCLGNBQUksaUJBQVcsTUFBTSxHQUFHO0FBQ3BCLGtCQUFNLGNBQWMsQ0FBQyxVQUFnQjtBQUNqQyxzQkFBUSxNQUFNLFFBQVEsT0FBTyxDQUFDLEtBQUssQ0FBQztBQUFBLFlBQ3hDO0FBQ0EsZ0JBQUksTUFBTSxRQUFRO0FBSWQsbUJBQUssYUFBYSxpQ0FBSyxVQUFMLEVBQWMsWUFBWSxJQUFHLEtBQUssRUFBRSxNQUFNLE1BQU07QUFBQSxZQUN0RSxPQUFPO0FBQ0gsc0JBQVEsY0FBYztBQUFBLFlBQzFCO0FBQUEsVUFDSjtBQUFBLFFBQ0osU0FBUTtBQUFBLFFBQUM7QUFFVCxjQUFNLFdBQW9DO0FBQUEsVUFDdEMsTUFBTSxNQUFNO0FBQUEsVUFDWixXQUFXO0FBQUEsVUFDWCxJQUFJLFVBQVU7QUFBRSxtQkFBTyxLQUFLLEtBQUs7QUFBQSxVQUFRO0FBQUEsVUFDekMsSUFBSSxRQUFRQyxRQUFPO0FBQUUsaUJBQUssS0FBSyxVQUFVQTtBQUFBLFVBQU87QUFBQSxVQUNoRCxJQUFJLFNBQVM7QUFBRSxtQkFBTyxLQUFLLEtBQUs7QUFBQSxVQUFPO0FBQUEsUUFDM0M7QUFFQSxjQUFNLFdBQVcsWUFBWSxTQUFTLFFBQVE7QUFDOUMsWUFBSTtBQUNBLGtCQUFRLE1BQU0sTUFBTSxPQUFPLENBQUMsWUFBWSxTQUFTLFFBQVEsR0FBRyxRQUFRLENBQUM7QUFBQSxRQUN6RSxTQUFTLEtBQUs7QUFDVixtQkFBUyxHQUFHO0FBQUEsUUFDaEI7QUFDQTtBQUFBLE1BQ0o7QUFBQSxJQUNKO0FBRUEsUUFBSSxNQUFNLFNBQVM7QUFBRTtBQUFBLElBQVE7QUFDN0IsVUFBTSxVQUFVO0FBQ2hCLFlBQVEsUUFBUSxLQUFLO0FBQUEsRUFDekI7QUFDSjtBQUtBLFNBQVMsWUFBZSxTQUE2QyxPQUE0RDtBQUM3SCxTQUFPLENBQUMsV0FBWTtBQUNoQixRQUFJLE1BQU0sV0FBVztBQUFFO0FBQUEsSUFBUTtBQUMvQixVQUFNLFlBQVk7QUFFbEIsUUFBSSxNQUFNLFNBQVM7QUFDZixVQUFJO0FBQ0EsWUFBSSxrQkFBa0IsZUFBZSxNQUFNLGtCQUFrQixlQUFlLE9BQU8sR0FBRyxPQUFPLE9BQU8sTUFBTSxPQUFPLEtBQUssR0FBRztBQUVySDtBQUFBLFFBQ0o7QUFBQSxNQUNKLFNBQVE7QUFBQSxNQUFDO0FBRVQsV0FBSyxRQUFRLE9BQU8sSUFBSSx3QkFBd0IsUUFBUSxTQUFTLE1BQU0sQ0FBQztBQUFBLElBQzVFLE9BQU87QUFDSCxZQUFNLFVBQVU7QUFDaEIsY0FBUSxPQUFPLE1BQU07QUFBQSxJQUN6QjtBQUFBLEVBQ0o7QUFDSjtBQU1BLFNBQVMsVUFBVSxRQUFxQyxRQUFlLE9BQTRCO0FBQy9GLFFBQU0sVUFBVSxDQUFDO0FBRWpCLGFBQVcsU0FBUyxRQUFRO0FBQ3hCLFFBQUk7QUFDSixRQUFJO0FBQ0EsVUFBSSxDQUFDLGlCQUFXLE1BQU0sSUFBSSxHQUFHO0FBQUU7QUFBQSxNQUFVO0FBQ3pDLGVBQVMsTUFBTTtBQUNmLFVBQUksQ0FBQyxpQkFBVyxNQUFNLEdBQUc7QUFBRTtBQUFBLE1BQVU7QUFBQSxJQUN6QyxTQUFRO0FBQUU7QUFBQSxJQUFVO0FBRXBCLFFBQUk7QUFDSixRQUFJO0FBQ0EsZUFBUyxRQUFRLE1BQU0sUUFBUSxPQUFPLENBQUMsS0FBSyxDQUFDO0FBQUEsSUFDakQsU0FBUyxLQUFLO0FBQ1YsY0FBUSxPQUFPLElBQUksd0JBQXdCLFFBQVEsS0FBSyx1Q0FBdUMsQ0FBQztBQUNoRztBQUFBLElBQ0o7QUFFQSxRQUFJLENBQUMsUUFBUTtBQUFFO0FBQUEsSUFBVTtBQUN6QixZQUFRO0FBQUEsT0FDSCxrQkFBa0IsVUFBVyxTQUFTLFFBQVEsUUFBUSxNQUFNLEdBQUcsTUFBTSxDQUFDLFdBQVk7QUFDL0UsZ0JBQVEsT0FBTyxJQUFJLHdCQUF3QixRQUFRLFFBQVEsdUNBQXVDLENBQUM7QUFBQSxNQUN2RyxDQUFDO0FBQUEsSUFDTDtBQUFBLEVBQ0o7QUFFQSxTQUFPLFFBQVEsSUFBSSxPQUFPO0FBQzlCO0FBS0EsU0FBUyxTQUFZLEdBQVM7QUFDMUIsU0FBTztBQUNYO0FBS0EsU0FBUyxRQUFRLFFBQXFCO0FBQ2xDLFFBQU07QUFDVjtBQUtBLFNBQVMsYUFBYSxLQUFrQjtBQUNwQyxNQUFJO0FBQ0EsUUFBSSxlQUFlLFNBQVMsT0FBTyxRQUFRLFlBQVksSUFBSSxhQUFhLE9BQU8sVUFBVSxVQUFVO0FBQy9GLGFBQU8sS0FBSztBQUFBLElBQ2hCO0FBQUEsRUFDSixTQUFRO0FBQUEsRUFBQztBQUVULE1BQUk7QUFDQSxXQUFPLEtBQUssVUFBVSxHQUFHO0FBQUEsRUFDN0IsU0FBUTtBQUFBLEVBQUM7QUFFVCxNQUFJO0FBQ0EsV0FBTyxPQUFPLFVBQVUsU0FBUyxLQUFLLEdBQUc7QUFBQSxFQUM3QyxTQUFRO0FBQUEsRUFBQztBQUVULFNBQU87QUFDWDtBQUtBLFNBQVMsZUFBa0IsU0FBK0M7QUE5NEIxRSxNQUFBRjtBQSs0QkksTUFBSSxPQUEyQ0EsTUFBQSxRQUFRLFVBQVUsTUFBbEIsT0FBQUEsTUFBdUIsQ0FBQztBQUN2RSxNQUFJLEVBQUUsYUFBYSxNQUFNO0FBQ3JCLFdBQU8sT0FBTyxLQUFLLHFCQUEyQixDQUFDO0FBQUEsRUFDbkQ7QUFDQSxNQUFJLFFBQVEsVUFBVSxLQUFLLE1BQU07QUFDN0IsUUFBSSxRQUFTO0FBQ2IsWUFBUSxVQUFVLElBQUk7QUFBQSxFQUMxQjtBQUNBLFNBQU8sSUFBSTtBQUNmO0FBR0EsSUFBSSx1QkFBdUIsUUFBUTtBQUNuQyxJQUFJLHdCQUF3QixPQUFPLHlCQUF5QixZQUFZO0FBQ3BFLHlCQUF1QixxQkFBcUIsS0FBSyxPQUFPO0FBQzVELE9BQU87QUFDSCx5QkFBdUIsV0FBd0M7QUFDM0QsUUFBSTtBQUNKLFFBQUk7QUFDSixVQUFNLFVBQVUsSUFBSSxRQUFXLENBQUMsS0FBSyxRQUFRO0FBQUUsZ0JBQVU7QUFBSyxlQUFTO0FBQUEsSUFBSyxDQUFDO0FBQzdFLFdBQU8sRUFBRSxTQUFTLFNBQVMsT0FBTztBQUFBLEVBQ3RDO0FBQ0o7OztBRnQ1QkEsT0FBTyxTQUFTLE9BQU8sVUFBVSxDQUFDO0FBQ2xDLE9BQU8sT0FBTyxvQkFBb0I7QUFDbEMsT0FBTyxPQUFPLG1CQUFtQjtBQUlqQyxJQUFNRyxRQUFPLGlCQUFpQixZQUFZLElBQUk7QUFDOUMsSUFBTSxhQUFhLGlCQUFpQixZQUFZLFVBQVU7QUFDMUQsSUFBTSxnQkFBZ0Isb0JBQUksSUFBOEI7QUFFeEQsSUFBTSxjQUFjO0FBQ3BCLElBQU0sZUFBZTtBQTBCZCxJQUFNLGVBQU4sY0FBMkIsTUFBTTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU1wQyxZQUFZLFNBQWtCLFNBQXdCO0FBQ2xELFVBQU0sU0FBUyxPQUFPO0FBQ3RCLFNBQUssT0FBTztBQUFBLEVBQ2hCO0FBQ0o7QUFTQSxTQUFTLGNBQWMsSUFBWSxNQUFjLFFBQXVCO0FBQ3BFLFFBQU0sWUFBWUMsc0JBQXFCLEVBQUU7QUFDekMsTUFBSSxDQUFDLFdBQVc7QUFDWjtBQUFBLEVBQ0o7QUFFQSxNQUFJLENBQUMsTUFBTTtBQUNQLGNBQVUsUUFBUSxNQUFTO0FBQUEsRUFDL0IsV0FBVyxDQUFDLFFBQVE7QUFDaEIsY0FBVSxRQUFRLElBQUk7QUFBQSxFQUMxQixPQUFPO0FBQ0gsUUFBSTtBQUNBLGdCQUFVLFFBQVEsS0FBSyxNQUFNLElBQUksQ0FBQztBQUFBLElBQ3RDLFNBQVMsS0FBVTtBQUNmLGdCQUFVLE9BQU8sSUFBSSxVQUFVLDZCQUE2QixJQUFJLFNBQVMsRUFBRSxPQUFPLElBQUksQ0FBQyxDQUFDO0FBQUEsSUFDNUY7QUFBQSxFQUNKO0FBQ0o7QUFTQSxTQUFTLGFBQWEsSUFBWSxNQUFjLFFBQXVCO0FBQ25FLFFBQU0sWUFBWUEsc0JBQXFCLEVBQUU7QUFDekMsTUFBSSxDQUFDLFdBQVc7QUFDWjtBQUFBLEVBQ0o7QUFFQSxNQUFJLENBQUMsUUFBUTtBQUNULGNBQVUsT0FBTyxJQUFJLE1BQU0sSUFBSSxDQUFDO0FBQUEsRUFDcEMsT0FBTztBQUNILFFBQUk7QUFDSixRQUFJO0FBQ0EsY0FBUSxLQUFLLE1BQU0sSUFBSTtBQUFBLElBQzNCLFNBQVMsS0FBVTtBQUNmLGdCQUFVLE9BQU8sSUFBSSxVQUFVLDRCQUE0QixJQUFJLFNBQVMsRUFBRSxPQUFPLElBQUksQ0FBQyxDQUFDO0FBQ3ZGO0FBQUEsSUFDSjtBQUVBLFFBQUksVUFBd0IsQ0FBQztBQUM3QixRQUFJLE1BQU0sT0FBTztBQUNiLGNBQVEsUUFBUSxNQUFNO0FBQUEsSUFDMUI7QUFFQSxRQUFJO0FBQ0osWUFBUSxNQUFNLE1BQU07QUFBQSxNQUNoQixLQUFLO0FBQ0Qsb0JBQVksSUFBSSxlQUFlLE1BQU0sU0FBUyxPQUFPO0FBQ3JEO0FBQUEsTUFDSixLQUFLO0FBQ0Qsb0JBQVksSUFBSSxVQUFVLE1BQU0sU0FBUyxPQUFPO0FBQ2hEO0FBQUEsTUFDSixLQUFLO0FBQ0Qsb0JBQVksSUFBSSxhQUFhLE1BQU0sU0FBUyxPQUFPO0FBQ25EO0FBQUEsTUFDSjtBQUNJLG9CQUFZLElBQUksTUFBTSxNQUFNLFNBQVMsT0FBTztBQUM1QztBQUFBLElBQ1I7QUFFQSxjQUFVLE9BQU8sU0FBUztBQUFBLEVBQzlCO0FBQ0o7QUFRQSxTQUFTQSxzQkFBcUIsSUFBMEM7QUFDcEUsUUFBTSxXQUFXLGNBQWMsSUFBSSxFQUFFO0FBQ3JDLGdCQUFjLE9BQU8sRUFBRTtBQUN2QixTQUFPO0FBQ1g7QUFPQSxTQUFTQyxjQUFxQjtBQUMxQixNQUFJO0FBQ0osS0FBRztBQUNDLGFBQVMsT0FBTztBQUFBLEVBQ3BCLFNBQVMsY0FBYyxJQUFJLE1BQU07QUFDakMsU0FBTztBQUNYO0FBY08sU0FBUyxLQUFLLFNBQStDO0FBQ2hFLFFBQU0sS0FBS0EsWUFBVztBQUV0QixRQUFNLFNBQVMsbUJBQW1CLGNBQW1CO0FBQ3JELGdCQUFjLElBQUksSUFBSSxFQUFFLFNBQVMsT0FBTyxTQUFTLFFBQVEsT0FBTyxPQUFPLENBQUM7QUFFeEUsUUFBTSxVQUFVRixNQUFLLGFBQWEsT0FBTyxPQUFPLEVBQUUsV0FBVyxHQUFHLEdBQUcsT0FBTyxDQUFDO0FBQzNFLE1BQUksVUFBVTtBQUVkLFVBQVEsS0FBSyxNQUFNO0FBQ2YsY0FBVTtBQUFBLEVBQ2QsR0FBRyxDQUFDLFFBQVE7QUFDUixrQkFBYyxPQUFPLEVBQUU7QUFDdkIsV0FBTyxPQUFPLEdBQUc7QUFBQSxFQUNyQixDQUFDO0FBRUQsUUFBTSxTQUFTLE1BQU07QUFDakIsa0JBQWMsT0FBTyxFQUFFO0FBQ3ZCLFdBQU8sV0FBVyxjQUFjLEVBQUMsV0FBVyxHQUFFLENBQUMsRUFBRSxNQUFNLENBQUMsUUFBUTtBQUM1RCxjQUFRLE1BQU0scURBQXFELEdBQUc7QUFBQSxJQUMxRSxDQUFDO0FBQUEsRUFDTDtBQUVBLFNBQU8sY0FBYyxNQUFNO0FBQ3ZCLFFBQUksU0FBUztBQUNULGFBQU8sT0FBTztBQUFBLElBQ2xCLE9BQU87QUFDSCxhQUFPLFFBQVEsS0FBSyxNQUFNO0FBQUEsSUFDOUI7QUFBQSxFQUNKO0FBRUEsU0FBTyxPQUFPO0FBQ2xCO0FBVU8sU0FBUyxPQUFPLGVBQXVCLE1BQXNDO0FBQ2hGLFNBQU8sS0FBSyxFQUFFLFlBQVksS0FBSyxDQUFDO0FBQ3BDO0FBVU8sU0FBUyxLQUFLLGFBQXFCLE1BQXNDO0FBQzVFLFNBQU8sS0FBSyxFQUFFLFVBQVUsS0FBSyxDQUFDO0FBQ2xDOzs7QUd4T0E7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQVlBLElBQU1HLFFBQU8saUJBQWlCLFlBQVksU0FBUztBQUVuRCxJQUFNLG1CQUFtQjtBQUN6QixJQUFNLGdCQUFnQjtBQVFmLFNBQVMsUUFBUSxNQUE2QjtBQUNqRCxTQUFPQSxNQUFLLGtCQUFrQixFQUFDLEtBQUksQ0FBQztBQUN4QztBQU9PLFNBQVMsT0FBd0I7QUFDcEMsU0FBT0EsTUFBSyxhQUFhO0FBQzdCOzs7QUNsQ0E7QUFBQTtBQUFBO0FBQUEsZUFBQUM7QUFBQSxFQUFBO0FBQUEsYUFBQUM7QUFBQSxFQUFBO0FBQUE7QUFBQTtBQWFPLFNBQVMsSUFBYSxRQUFnQjtBQUN6QyxTQUFPO0FBQ1g7QUFNTyxTQUFTLFVBQVUsUUFBcUI7QUFDM0MsU0FBUyxVQUFVLE9BQVEsS0FBSztBQUNwQztBQU9PLFNBQVNDLE9BQWUsU0FBbUQ7QUFDOUUsTUFBSSxZQUFZLEtBQUs7QUFDakIsV0FBTyxDQUFDLFdBQVksV0FBVyxPQUFPLENBQUMsSUFBSTtBQUFBLEVBQy9DO0FBRUEsU0FBTyxDQUFDLFdBQVc7QUFDZixRQUFJLFdBQVcsTUFBTTtBQUNqQixhQUFPLENBQUM7QUFBQSxJQUNaO0FBQ0EsYUFBUyxJQUFJLEdBQUcsSUFBSSxPQUFPLFFBQVEsS0FBSztBQUNwQyxhQUFPLENBQUMsSUFBSSxRQUFRLE9BQU8sQ0FBQyxDQUFDO0FBQUEsSUFDakM7QUFDQSxXQUFPO0FBQUEsRUFDWDtBQUNKO0FBT08sU0FBU0MsS0FBYSxLQUE4QixPQUErRDtBQUN0SCxNQUFJLFVBQVUsS0FBSztBQUNmLFdBQU8sQ0FBQyxXQUFZLFdBQVcsT0FBTyxDQUFDLElBQUk7QUFBQSxFQUMvQztBQUVBLFNBQU8sQ0FBQyxXQUFXO0FBQ2YsUUFBSSxXQUFXLE1BQU07QUFDakIsYUFBTyxDQUFDO0FBQUEsSUFDWjtBQUNBLGVBQVdDLFFBQU8sUUFBUTtBQUN0QixhQUFPQSxJQUFHLElBQUksTUFBTSxPQUFPQSxJQUFHLENBQUM7QUFBQSxJQUNuQztBQUNBLFdBQU87QUFBQSxFQUNYO0FBQ0o7QUFNTyxTQUFTLFNBQWtCLFNBQTBEO0FBQ3hGLE1BQUksWUFBWSxLQUFLO0FBQ2pCLFdBQU87QUFBQSxFQUNYO0FBRUEsU0FBTyxDQUFDLFdBQVksV0FBVyxPQUFPLE9BQU8sUUFBUSxNQUFNO0FBQy9EO0FBTU8sU0FBUyxPQUFPLGFBRXZCO0FBQ0ksTUFBSSxTQUFTO0FBQ2IsYUFBVyxRQUFRLGFBQWE7QUFDNUIsUUFBSSxZQUFZLElBQUksTUFBTSxLQUFLO0FBQzNCLGVBQVM7QUFDVDtBQUFBLElBQ0o7QUFBQSxFQUNKO0FBQ0EsTUFBSSxRQUFRO0FBQ1IsV0FBTztBQUFBLEVBQ1g7QUFFQSxTQUFPLENBQUMsV0FBVztBQUNmLGVBQVcsUUFBUSxhQUFhO0FBQzVCLFVBQUksUUFBUSxRQUFRO0FBQ2hCLGVBQU8sSUFBSSxJQUFJLFlBQVksSUFBSSxFQUFFLE9BQU8sSUFBSSxDQUFDO0FBQUEsTUFDakQ7QUFBQSxJQUNKO0FBQ0EsV0FBTztBQUFBLEVBQ1g7QUFDSjs7O0FDekdBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQXdEQSxJQUFNQyxRQUFPLGlCQUFpQixZQUFZLE9BQU87QUFFakQsSUFBTSxTQUFTO0FBQ2YsSUFBTSxhQUFhO0FBQ25CLElBQU0sYUFBYTtBQU9aLFNBQVMsU0FBNEI7QUFDeEMsU0FBT0EsTUFBSyxNQUFNO0FBQ3RCO0FBT08sU0FBUyxhQUE4QjtBQUMxQyxTQUFPQSxNQUFLLFVBQVU7QUFDMUI7QUFPTyxTQUFTLGFBQThCO0FBQzFDLFNBQU9BLE1BQUssVUFBVTtBQUMxQjs7O0F0QjVFQSxPQUFPLFNBQVMsT0FBTyxVQUFVLENBQUM7QUE0Q2xDLE9BQU8sT0FBTyxTQUFnQjtBQUN2QixPQUFPLHFCQUFxQjsiLAogICJuYW1lcyI6IFsiX2EiLCAiRXJyb3IiLCAiY2FsbCIsICJfYSIsICJFcnJvciIsICJjYWxsIiwgIl9hIiwgInJlc2l6YWJsZSIsICJjYWxsIiwgIl9hIiwgImNhbGwiLCAiY2FsbCIsICJIaWRlTWV0aG9kIiwgIlNob3dNZXRob2QiLCAiaXNEb2N1bWVudERvdEFsbCIsICJfYSIsICJyZWFzb24iLCAidmFsdWUiLCAiY2FsbCIsICJnZXRBbmREZWxldGVSZXNwb25zZSIsICJnZW5lcmF0ZUlEIiwgImNhbGwiLCAiQXJyYXkiLCAiTWFwIiwgIkFycmF5IiwgIk1hcCIsICJrZXkiLCAiY2FsbCJdCn0K diff --git a/v3/internal/assetserver/bundledassets/runtime.js b/v3/internal/assetserver/bundledassets/runtime.js new file mode 100644 index 000000000..b24798ef7 --- /dev/null +++ b/v3/internal/assetserver/bundledassets/runtime.js @@ -0,0 +1 @@ +var Ue=Object.defineProperty,dn=Object.defineProperties;var mn=Object.getOwnPropertyDescriptors;var Le=Object.getOwnPropertySymbols;var un=Object.prototype.hasOwnProperty,wn=Object.prototype.propertyIsEnumerable;var ze=(n,e,i)=>e in n?Ue(n,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):n[e]=i,Ie=(n,e)=>{for(var i in e||(e={}))un.call(e,i)&&ze(n,i,e[i]);if(Le)for(var i of Le(e))wn.call(e,i)&&ze(n,i,e[i]);return n},je=(n,e)=>dn(n,mn(e));var p=(n,e)=>{for(var i in e)Ue(n,i,{get:e[i],enumerable:!0})};var me={};p(me,{Application:()=>Pe,Browser:()=>ee,Call:()=>Re,CancelError:()=>F,CancellablePromise:()=>H,CancelledRejectionError:()=>b,Clipboard:()=>Ee,Create:()=>Oe,Dialogs:()=>ie,Events:()=>re,Flags:()=>ge,Screens:()=>Fe,System:()=>fe,WML:()=>de,Window:()=>G});var de={};p(de,{Enable:()=>ce,Reload:()=>$e});var ee={};p(ee,{OpenURL:()=>q});var pn="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function D(n=21){let e="",i=n|0;for(;i--;)e+=pn[Math.random()*64|0];return e}var fn=window.location.origin+"/wails/runtime",d=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10}),gn=D();function m(n,e=""){return function(i,o=null){return hn(n,i,e,o)}}async function hn(n,e,i,o){var l,c;let t=new URL(fn);t.searchParams.append("object",n.toString()),t.searchParams.append("method",e.toString()),o&&t.searchParams.append("args",JSON.stringify(o));let r={"x-wails-client-id":gn};i&&(r["x-wails-window-name"]=i);let a=await fetch(t,{headers:r});if(!a.ok)throw new Error(await a.text());return((c=(l=a.headers.get("Content-Type"))==null?void 0:l.indexOf("application/json"))!=null?c:-1)!==-1?a.json():a.text()}var Wn=m(d.Browser),bn=0;function q(n){return Wn(bn,{url:n.toString()})}var ie={};p(ie,{Error:()=>On,Info:()=>Rn,OpenFile:()=>kn,Question:()=>ne,SaveFile:()=>Fn,Warning:()=>En});window._wails=window._wails||{};window._wails.dialogErrorCallback=xn;window._wails.dialogResultCallback=Sn;var vn=m(d.Dialog),z=new Map,yn=0,Cn=1,Pn=2,Dn=3,Tn=4,Mn=5;function Sn(n,e,i){let o=Be(n);if(o)if(i)try{o.resolve(JSON.parse(e))}catch(t){o.reject(new TypeError("could not parse result: "+t.message,{cause:t}))}else o.resolve(e)}function xn(n,e){var i;(i=Be(n))==null||i.reject(new window.Error(e))}function Be(n){let e=z.get(n);return z.delete(n),e}function An(){let n;do n=D();while(z.has(n));return n}function T(n,e={}){let i=An();return new Promise((o,t)=>{z.set(i,{resolve:o,reject:t}),vn(n,Object.assign({"dialog-id":i},e)).catch(r=>{z.delete(i),t(r)})})}function Rn(n){return T(yn,n)}function En(n){return T(Cn,n)}function On(n){return T(Pn,n)}function ne(n){return T(Dn,n)}function kn(n){var e;return(e=T(Tn,n))!=null?e:[]}function Fn(n){return T(Mn,n)}var re={};p(re,{Emit:()=>te,Off:()=>Bn,OffAll:()=>Hn,On:()=>In,OnMultiple:()=>oe,Once:()=>jn,Types:()=>Ne,WailsEvent:()=>M});var g=new Map,N=class{constructor(e,i,o){this.eventName=e,this.callback=i,this.maxCallbacks=o||-1}dispatch(e){try{this.callback(e)}catch(i){}return this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0)}};function He(n){let e=g.get(n.eventName);e&&(e=e.filter(i=>i!==n),e.length===0?g.delete(n.eventName):g.set(n.eventName,e))}var Ne=Object.freeze({Windows:Object.freeze({APMPowerSettingChange:"windows:APMPowerSettingChange",APMPowerStatusChange:"windows:APMPowerStatusChange",APMResumeAutomatic:"windows:APMResumeAutomatic",APMResumeSuspend:"windows:APMResumeSuspend",APMSuspend:"windows:APMSuspend",ApplicationStarted:"windows:ApplicationStarted",SystemThemeChanged:"windows:SystemThemeChanged",WebViewNavigationCompleted:"windows:WebViewNavigationCompleted",WindowActive:"windows:WindowActive",WindowBackgroundErase:"windows:WindowBackgroundErase",WindowClickActive:"windows:WindowClickActive",WindowClosing:"windows:WindowClosing",WindowDidMove:"windows:WindowDidMove",WindowDidResize:"windows:WindowDidResize",WindowDPIChanged:"windows:WindowDPIChanged",WindowDragDrop:"windows:WindowDragDrop",WindowDragEnter:"windows:WindowDragEnter",WindowDragLeave:"windows:WindowDragLeave",WindowDragOver:"windows:WindowDragOver",WindowEndMove:"windows:WindowEndMove",WindowEndResize:"windows:WindowEndResize",WindowFullscreen:"windows:WindowFullscreen",WindowHide:"windows:WindowHide",WindowInactive:"windows:WindowInactive",WindowKeyDown:"windows:WindowKeyDown",WindowKeyUp:"windows:WindowKeyUp",WindowKillFocus:"windows:WindowKillFocus",WindowNonClientHit:"windows:WindowNonClientHit",WindowNonClientMouseDown:"windows:WindowNonClientMouseDown",WindowNonClientMouseLeave:"windows:WindowNonClientMouseLeave",WindowNonClientMouseMove:"windows:WindowNonClientMouseMove",WindowNonClientMouseUp:"windows:WindowNonClientMouseUp",WindowPaint:"windows:WindowPaint",WindowRestore:"windows:WindowRestore",WindowSetFocus:"windows:WindowSetFocus",WindowShow:"windows:WindowShow",WindowStartMove:"windows:WindowStartMove",WindowStartResize:"windows:WindowStartResize",WindowUnFullscreen:"windows:WindowUnFullscreen",WindowZOrderChanged:"windows:WindowZOrderChanged",WindowMinimise:"windows:WindowMinimise",WindowUnMinimise:"windows:WindowUnMinimise",WindowMaximise:"windows:WindowMaximise",WindowUnMaximise:"windows:WindowUnMaximise"}),Mac:Object.freeze({ApplicationDidBecomeActive:"mac:ApplicationDidBecomeActive",ApplicationDidChangeBackingProperties:"mac:ApplicationDidChangeBackingProperties",ApplicationDidChangeEffectiveAppearance:"mac:ApplicationDidChangeEffectiveAppearance",ApplicationDidChangeIcon:"mac:ApplicationDidChangeIcon",ApplicationDidChangeOcclusionState:"mac:ApplicationDidChangeOcclusionState",ApplicationDidChangeScreenParameters:"mac:ApplicationDidChangeScreenParameters",ApplicationDidChangeStatusBarFrame:"mac:ApplicationDidChangeStatusBarFrame",ApplicationDidChangeStatusBarOrientation:"mac:ApplicationDidChangeStatusBarOrientation",ApplicationDidChangeTheme:"mac:ApplicationDidChangeTheme",ApplicationDidFinishLaunching:"mac:ApplicationDidFinishLaunching",ApplicationDidHide:"mac:ApplicationDidHide",ApplicationDidResignActive:"mac:ApplicationDidResignActive",ApplicationDidUnhide:"mac:ApplicationDidUnhide",ApplicationDidUpdate:"mac:ApplicationDidUpdate",ApplicationShouldHandleReopen:"mac:ApplicationShouldHandleReopen",ApplicationWillBecomeActive:"mac:ApplicationWillBecomeActive",ApplicationWillFinishLaunching:"mac:ApplicationWillFinishLaunching",ApplicationWillHide:"mac:ApplicationWillHide",ApplicationWillResignActive:"mac:ApplicationWillResignActive",ApplicationWillTerminate:"mac:ApplicationWillTerminate",ApplicationWillUnhide:"mac:ApplicationWillUnhide",ApplicationWillUpdate:"mac:ApplicationWillUpdate",MenuDidAddItem:"mac:MenuDidAddItem",MenuDidBeginTracking:"mac:MenuDidBeginTracking",MenuDidClose:"mac:MenuDidClose",MenuDidDisplayItem:"mac:MenuDidDisplayItem",MenuDidEndTracking:"mac:MenuDidEndTracking",MenuDidHighlightItem:"mac:MenuDidHighlightItem",MenuDidOpen:"mac:MenuDidOpen",MenuDidPopUp:"mac:MenuDidPopUp",MenuDidRemoveItem:"mac:MenuDidRemoveItem",MenuDidSendAction:"mac:MenuDidSendAction",MenuDidSendActionToItem:"mac:MenuDidSendActionToItem",MenuDidUpdate:"mac:MenuDidUpdate",MenuWillAddItem:"mac:MenuWillAddItem",MenuWillBeginTracking:"mac:MenuWillBeginTracking",MenuWillDisplayItem:"mac:MenuWillDisplayItem",MenuWillEndTracking:"mac:MenuWillEndTracking",MenuWillHighlightItem:"mac:MenuWillHighlightItem",MenuWillOpen:"mac:MenuWillOpen",MenuWillPopUp:"mac:MenuWillPopUp",MenuWillRemoveItem:"mac:MenuWillRemoveItem",MenuWillSendAction:"mac:MenuWillSendAction",MenuWillSendActionToItem:"mac:MenuWillSendActionToItem",MenuWillUpdate:"mac:MenuWillUpdate",WebViewDidCommitNavigation:"mac:WebViewDidCommitNavigation",WebViewDidFinishNavigation:"mac:WebViewDidFinishNavigation",WebViewDidReceiveServerRedirectForProvisionalNavigation:"mac:WebViewDidReceiveServerRedirectForProvisionalNavigation",WebViewDidStartProvisionalNavigation:"mac:WebViewDidStartProvisionalNavigation",WindowDidBecomeKey:"mac:WindowDidBecomeKey",WindowDidBecomeMain:"mac:WindowDidBecomeMain",WindowDidBeginSheet:"mac:WindowDidBeginSheet",WindowDidChangeAlpha:"mac:WindowDidChangeAlpha",WindowDidChangeBackingLocation:"mac:WindowDidChangeBackingLocation",WindowDidChangeBackingProperties:"mac:WindowDidChangeBackingProperties",WindowDidChangeCollectionBehavior:"mac:WindowDidChangeCollectionBehavior",WindowDidChangeEffectiveAppearance:"mac:WindowDidChangeEffectiveAppearance",WindowDidChangeOcclusionState:"mac:WindowDidChangeOcclusionState",WindowDidChangeOrderingMode:"mac:WindowDidChangeOrderingMode",WindowDidChangeScreen:"mac:WindowDidChangeScreen",WindowDidChangeScreenParameters:"mac:WindowDidChangeScreenParameters",WindowDidChangeScreenProfile:"mac:WindowDidChangeScreenProfile",WindowDidChangeScreenSpace:"mac:WindowDidChangeScreenSpace",WindowDidChangeScreenSpaceProperties:"mac:WindowDidChangeScreenSpaceProperties",WindowDidChangeSharingType:"mac:WindowDidChangeSharingType",WindowDidChangeSpace:"mac:WindowDidChangeSpace",WindowDidChangeSpaceOrderingMode:"mac:WindowDidChangeSpaceOrderingMode",WindowDidChangeTitle:"mac:WindowDidChangeTitle",WindowDidChangeToolbar:"mac:WindowDidChangeToolbar",WindowDidDeminiaturize:"mac:WindowDidDeminiaturize",WindowDidEndSheet:"mac:WindowDidEndSheet",WindowDidEnterFullScreen:"mac:WindowDidEnterFullScreen",WindowDidEnterVersionBrowser:"mac:WindowDidEnterVersionBrowser",WindowDidExitFullScreen:"mac:WindowDidExitFullScreen",WindowDidExitVersionBrowser:"mac:WindowDidExitVersionBrowser",WindowDidExpose:"mac:WindowDidExpose",WindowDidFocus:"mac:WindowDidFocus",WindowDidMiniaturize:"mac:WindowDidMiniaturize",WindowDidMove:"mac:WindowDidMove",WindowDidOrderOffScreen:"mac:WindowDidOrderOffScreen",WindowDidOrderOnScreen:"mac:WindowDidOrderOnScreen",WindowDidResignKey:"mac:WindowDidResignKey",WindowDidResignMain:"mac:WindowDidResignMain",WindowDidResize:"mac:WindowDidResize",WindowDidUpdate:"mac:WindowDidUpdate",WindowDidUpdateAlpha:"mac:WindowDidUpdateAlpha",WindowDidUpdateCollectionBehavior:"mac:WindowDidUpdateCollectionBehavior",WindowDidUpdateCollectionProperties:"mac:WindowDidUpdateCollectionProperties",WindowDidUpdateShadow:"mac:WindowDidUpdateShadow",WindowDidUpdateTitle:"mac:WindowDidUpdateTitle",WindowDidUpdateToolbar:"mac:WindowDidUpdateToolbar",WindowDidZoom:"mac:WindowDidZoom",WindowFileDraggingEntered:"mac:WindowFileDraggingEntered",WindowFileDraggingExited:"mac:WindowFileDraggingExited",WindowFileDraggingPerformed:"mac:WindowFileDraggingPerformed",WindowHide:"mac:WindowHide",WindowMaximise:"mac:WindowMaximise",WindowUnMaximise:"mac:WindowUnMaximise",WindowMinimise:"mac:WindowMinimise",WindowUnMinimise:"mac:WindowUnMinimise",WindowShouldClose:"mac:WindowShouldClose",WindowShow:"mac:WindowShow",WindowWillBecomeKey:"mac:WindowWillBecomeKey",WindowWillBecomeMain:"mac:WindowWillBecomeMain",WindowWillBeginSheet:"mac:WindowWillBeginSheet",WindowWillChangeOrderingMode:"mac:WindowWillChangeOrderingMode",WindowWillClose:"mac:WindowWillClose",WindowWillDeminiaturize:"mac:WindowWillDeminiaturize",WindowWillEnterFullScreen:"mac:WindowWillEnterFullScreen",WindowWillEnterVersionBrowser:"mac:WindowWillEnterVersionBrowser",WindowWillExitFullScreen:"mac:WindowWillExitFullScreen",WindowWillExitVersionBrowser:"mac:WindowWillExitVersionBrowser",WindowWillFocus:"mac:WindowWillFocus",WindowWillMiniaturize:"mac:WindowWillMiniaturize",WindowWillMove:"mac:WindowWillMove",WindowWillOrderOffScreen:"mac:WindowWillOrderOffScreen",WindowWillOrderOnScreen:"mac:WindowWillOrderOnScreen",WindowWillResignMain:"mac:WindowWillResignMain",WindowWillResize:"mac:WindowWillResize",WindowWillUnfocus:"mac:WindowWillUnfocus",WindowWillUpdate:"mac:WindowWillUpdate",WindowWillUpdateAlpha:"mac:WindowWillUpdateAlpha",WindowWillUpdateCollectionBehavior:"mac:WindowWillUpdateCollectionBehavior",WindowWillUpdateCollectionProperties:"mac:WindowWillUpdateCollectionProperties",WindowWillUpdateShadow:"mac:WindowWillUpdateShadow",WindowWillUpdateTitle:"mac:WindowWillUpdateTitle",WindowWillUpdateToolbar:"mac:WindowWillUpdateToolbar",WindowWillUpdateVisibility:"mac:WindowWillUpdateVisibility",WindowWillUseStandardFrame:"mac:WindowWillUseStandardFrame",WindowZoomIn:"mac:WindowZoomIn",WindowZoomOut:"mac:WindowZoomOut",WindowZoomReset:"mac:WindowZoomReset"}),Linux:Object.freeze({ApplicationStartup:"linux:ApplicationStartup",SystemThemeChanged:"linux:SystemThemeChanged",WindowDeleteEvent:"linux:WindowDeleteEvent",WindowDidMove:"linux:WindowDidMove",WindowDidResize:"linux:WindowDidResize",WindowFocusIn:"linux:WindowFocusIn",WindowFocusOut:"linux:WindowFocusOut",WindowLoadChanged:"linux:WindowLoadChanged"}),Common:Object.freeze({ApplicationOpenedWithFile:"common:ApplicationOpenedWithFile",ApplicationStarted:"common:ApplicationStarted",ApplicationLaunchedWithUrl:"common:ApplicationLaunchedWithUrl",ThemeChanged:"common:ThemeChanged",WindowClosing:"common:WindowClosing",WindowDidMove:"common:WindowDidMove",WindowDidResize:"common:WindowDidResize",WindowDPIChanged:"common:WindowDPIChanged",WindowFilesDropped:"common:WindowFilesDropped",WindowFocus:"common:WindowFocus",WindowFullscreen:"common:WindowFullscreen",WindowHide:"common:WindowHide",WindowLostFocus:"common:WindowLostFocus",WindowMaximise:"common:WindowMaximise",WindowMinimise:"common:WindowMinimise",WindowToggleFrameless:"common:WindowToggleFrameless",WindowRestore:"common:WindowRestore",WindowRuntimeReady:"common:WindowRuntimeReady",WindowShow:"common:WindowShow",WindowUnFullscreen:"common:WindowUnFullscreen",WindowUnMaximise:"common:WindowUnMaximise",WindowUnMinimise:"common:WindowUnMinimise",WindowZoom:"common:WindowZoom",WindowZoomIn:"common:WindowZoomIn",WindowZoomOut:"common:WindowZoomOut",WindowZoomReset:"common:WindowZoomReset",WindowDropZoneFilesDropped:"common:WindowDropZoneFilesDropped"})});window._wails=window._wails||{};window._wails.dispatchWailsEvent=Un;var Ln=m(d.Events),zn=0,M=class{constructor(e,i=null){this.name=e,this.data=i}};function Un(n){let e=g.get(n.name);if(!e)return;let i=new M(n.name,n.data);"sender"in n&&(i.sender=n.sender),e=e.filter(o=>!o.dispatch(i)),e.length===0?g.delete(n.name):g.set(n.name,e)}function oe(n,e,i){let o=g.get(n)||[],t=new N(n,e,i);return o.push(t),g.set(n,o),()=>He(t)}function In(n,e){return oe(n,e,-1)}function jn(n,e){return oe(n,e,1)}function Bn(...n){n.forEach(e=>g.delete(e))}function Hn(){g.clear()}function te(n,e){let i,o;return typeof n=="object"&&n!==null&&"name"in n&&"data"in n?(i=n.name,o=n.data):(i=n,o=e),Ln(zn,{name:i,data:o})}function Ze(){return new MouseEvent("mousedown").buttons===0}function Ve(){if(!EventTarget||!AbortSignal||!AbortController)return!1;let n=!0,e=new EventTarget,i=new AbortController;return e.addEventListener("test",()=>{n=!1},{signal:i.signal}),i.abort(),e.dispatchEvent(new CustomEvent("test")),n}function Z(n){var e;return n.target instanceof HTMLElement?n.target:!(n.target instanceof HTMLElement)&&n.target instanceof Node&&(e=n.target.parentElement)!=null?e:document.body}var Ge=!1;document.addEventListener("DOMContentLoaded",()=>{Ge=!0});function Ke(n){Ge||document.readyState==="complete"?n():document.addEventListener("DOMContentLoaded",n)}var Nn="data-wails-dropzone",S="wails-dropzone-hover",u=null,Zn=0,Vn=1,Gn=2,Kn=3,Yn=4,Xn=5,_n=6,$n=7,Jn=8,Qn=9,qn=10,ei=11,ni=12,ii=13,oi=14,ti=15,ri=16,si=17,ai=18,li=19,ci=20,di=21,mi=22,ui=23,wi=24,pi=25,fi=26,gi=27,hi=28,Wi=29,bi=30,vi=31,yi=32,Ci=33,Pi=34,Di=35,Ti=36,Mi=37,Si=38,xi=39,Ai=40,Ri=41,Ei=42,Oi=43,ki=44,Fi=45,Li=46,zi=47,Ui=48,Ii=49;function Ye(n){return n?n.closest("[".concat(Nn,"]")):null}var s=Symbol("caller");s;var V=class V{constructor(e=""){this[s]=m(d.Window,e);for(let i of Object.getOwnPropertyNames(V.prototype))i!=="constructor"&&typeof this[i]=="function"&&(this[i]=this[i].bind(this))}Get(e){return new V(e)}Position(){return this[s](Zn)}Center(){return this[s](Vn)}Close(){return this[s](Gn)}DisableSizeConstraints(){return this[s](Kn)}EnableSizeConstraints(){return this[s](Yn)}Focus(){return this[s](Xn)}ForceReload(){return this[s](_n)}Fullscreen(){return this[s]($n)}GetScreen(){return this[s](Jn)}GetZoom(){return this[s](Qn)}Height(){return this[s](qn)}Hide(){return this[s](ei)}IsFocused(){return this[s](ni)}IsFullscreen(){return this[s](ii)}IsMaximised(){return this[s](oi)}IsMinimised(){return this[s](ti)}Maximise(){return this[s](ri)}Minimise(){return this[s](si)}Name(){return this[s](ai)}OpenDevTools(){return this[s](li)}RelativePosition(){return this[s](ci)}Reload(){return this[s](di)}Resizable(){return this[s](mi)}Restore(){return this[s](ui)}SetPosition(e,i){return this[s](wi,{x:e,y:i})}SetAlwaysOnTop(e){return this[s](pi,{alwaysOnTop:e})}SetBackgroundColour(e,i,o,t){return this[s](fi,{r:e,g:i,b:o,a:t})}SetFrameless(e){return this[s](gi,{frameless:e})}SetFullscreenButtonEnabled(e){return this[s](hi,{enabled:e})}SetMaxSize(e,i){return this[s](Wi,{width:e,height:i})}SetMinSize(e,i){return this[s](bi,{width:e,height:i})}SetRelativePosition(e,i){return this[s](vi,{x:e,y:i})}SetResizable(e){return this[s](yi,{resizable:e})}SetSize(e,i){return this[s](Ci,{width:e,height:i})}SetTitle(e){return this[s](Pi,{title:e})}SetZoom(e){return this[s](Di,{zoom:e})}Show(){return this[s](Ti)}Size(){return this[s](Mi)}ToggleFullscreen(){return this[s](Si)}ToggleMaximise(){return this[s](xi)}ToggleFrameless(){return this[s](Ai)}UnFullscreen(){return this[s](Ri)}UnMaximise(){return this[s](Ei)}UnMinimise(){return this[s](Oi)}Width(){return this[s](ki)}Zoom(){return this[s](Fi)}ZoomIn(){return this[s](Li)}ZoomOut(){return this[s](zi)}ZoomReset(){return this[s](Ui)}HandlePlatformFileDrop(e,i,o){let t=document.elementFromPoint(i,o),r=Ye(t);if(!r)return;let a={id:r.id,classList:Array.from(r.classList),attributes:{}};for(let c=0;c{if(i.preventDefault(),i.dataTransfer&&i.dataTransfer.types.includes("Files")){e++;let o=document.elementFromPoint(i.clientX,i.clientY),t=Ye(o);u&&u!==t&&u.classList.remove(S),t?(t.classList.add(S),i.dataTransfer.dropEffect="copy",u=t):(i.dataTransfer.dropEffect="none",u=null)}},!1),n.addEventListener("dragover",i=>{i.preventDefault(),i.dataTransfer&&i.dataTransfer.types.includes("Files")&&(u?(u.classList.contains(S)||u.classList.add(S),i.dataTransfer.dropEffect="copy"):i.dataTransfer.dropEffect="none")},!1),n.addEventListener("dragleave",i=>{i.preventDefault(),i.dataTransfer&&i.dataTransfer.types.includes("Files")&&(e--,(e===0||i.relatedTarget===null||u&&!u.contains(i.relatedTarget))&&(u&&(u.classList.remove(S),u=null),e=0))},!1),n.addEventListener("drop",i=>{i.preventDefault(),e=0,u&&(u.classList.remove(S),u=null)},!1)}typeof window<"u"&&typeof document<"u"&&Bi();var G=ji;function Hi(n,e=null){te(new M(n,e))}function Ni(n,e){let i=G.Get(n),o=i[e];if(typeof o=="function")try{o.call(i)}catch(t){}}function Xe(n){let e=n.currentTarget;function i(t="Yes"){if(t!=="Yes")return;let r=e.getAttribute("wml-event")||e.getAttribute("data-wml-event"),a=e.getAttribute("wml-target-window")||e.getAttribute("data-wml-target-window")||"",l=e.getAttribute("wml-window")||e.getAttribute("data-wml-window"),c=e.getAttribute("wml-openurl")||e.getAttribute("data-wml-openurl");r!==null&&Hi(r),l!==null&&Ni(a,l),c!==null&&q(c)}let o=e.getAttribute("wml-confirm")||e.getAttribute("data-wml-confirm");o?ne({Title:"Confirm",Message:o,Detached:!1,Buttons:[{Label:"Yes"},{Label:"No",IsDefault:!0}]}).then(i):i()}var U=Symbol("controller"),x=Symbol("triggerMap"),v=Symbol("elementCount");U;var ae=class{constructor(){this[U]=new AbortController}set(e,i){return{signal:this[U].signal}}reset(){this[U].abort(),this[U]=new AbortController}};x,v;var le=class{constructor(){this[x]=new WeakMap,this[v]=0}set(e,i){return this[x].has(e)||this[v]++,this[x].set(e,i),{}}reset(){if(!(this[v]<=0)){for(let e of document.body.querySelectorAll("*")){if(this[v]<=0)break;let i=this[x].get(e);i!=null&&this[v]--;for(let o of i||[])e.removeEventListener(o,Xe)}this[x]=new WeakMap,this[v]=0}}},_e=Ve()?new ae:new le;function Zi(n){let e=/\S+/g,i=n.getAttribute("wml-trigger")||n.getAttribute("data-wml-trigger")||"click",o=[],t;for(;(t=e.exec(i))!==null;)o.push(t[0]);let r=_e.set(n,o);for(let a of o)n.addEventListener(a,Xe,r)}function ce(){Ke($e)}function $e(){_e.reset(),document.body.querySelectorAll("[wml-event], [wml-window], [wml-openurl], [data-wml-event], [data-wml-window], [data-wml-openurl]").forEach(Zi)}window.wails=me;ce();var fe={};p(fe,{Capabilities:()=>Xi,Environment:()=>_i,HandlePlatformFileDrop:()=>no,IsAMD64:()=>Qi,IsARM:()=>qi,IsARM64:()=>eo,IsDarkMode:()=>Yi,IsDebug:()=>pe,IsLinux:()=>$i,IsMac:()=>Ji,IsWindows:()=>K,invoke:()=>y});var we=m(d.System),Vi=0,Gi=1,Ki=100,ue=function(){var n,e,i,o,t;try{if((e=(n=window.chrome)==null?void 0:n.webview)!=null&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if((t=(o=(i=window.webkit)==null?void 0:i.messageHandlers)==null?void 0:o.external)!=null&&t.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch(r){}return null}();function y(n){ue==null||ue(n)}function Yi(){return we(Vi)}async function Xi(){let n=await fetch("/wails/capabilities");if(n.ok)return n.json();throw new Error("could not fetch capabilities: "+n.statusText)}function _i(){return we(Gi)}function K(){return window._wails.environment.OS==="windows"}function $i(){return window._wails.environment.OS==="linux"}function Ji(){return window._wails.environment.OS==="darwin"}function Qi(){return window._wails.environment.Arch==="amd64"}function qi(){return window._wails.environment.Arch==="arm"}function eo(){return window._wails.environment.Arch==="arm64"}function pe(){return!!window._wails.environment.Debug}function no(n,e,i){let o=document.elementFromPoint(e,i),t=o?o.id:"",r=o?Array.from(o.classList):[];we(Ki,{filenames:n,x:e,y:i,elementId:t,classList:r}).then(()=>{}).catch(l=>{})}window.addEventListener("contextmenu",ro);var io=m(d.ContextMenu),oo=0;function to(n,e,i,o){io(oo,{id:n,x:e,y:i,data:o})}function ro(n){let e=Z(n),i=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(i){n.preventDefault();let o=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");to(i,n.clientX,n.clientY,o)}else so(n,e)}function so(n,e){if(pe())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":n.preventDefault();return}if(e.isContentEditable)return;let i=window.getSelection(),o=i&&i.toString().length>0;if(o)for(let t=0;tI});function I(n){try{return window._wails.flags[n]}catch(e){throw new Error("Unable to retrieve flag '"+n+"': "+e,{cause:e})}}var j=!1,B=!1,ve=!1,R=!1,E=!1,C="",Je="auto",A=0,he=Ze();window._wails=window._wails||{};window._wails.setResizable=n=>{ve=n,ve||(R=E=!1,f())};window.addEventListener("mousedown",ye,{capture:!0});window.addEventListener("mousemove",ye,{capture:!0});window.addEventListener("mouseup",ye,{capture:!0});for(let n of["click","contextmenu","dblclick"])window.addEventListener(n,ao,{capture:!0});function ao(n){(B||E)&&(n.stopImmediatePropagation(),n.stopPropagation(),n.preventDefault())}var We=0,lo=1,be=2;function ye(n){let e,i=n.buttons;switch(n.type){case"mousedown":e=We,he||(i=A|1<ho,Quit:()=>bo,Show:()=>Wo});var Ce=m(d.Application),po=0,fo=1,go=2;function ho(){return Ce(po)}function Wo(){return Ce(fo)}function bo(){return Ce(go)}var Re={};p(Re,{ByID:()=>Ho,ByName:()=>Bo,Call:()=>Ae,RuntimeError:()=>$});var qe=Function.prototype.toString,O=typeof Reflect=="object"&&Reflect!==null&&Reflect.apply,De,Y;if(typeof O=="function"&&typeof Object.defineProperty=="function")try{De=Object.defineProperty({},"length",{get:function(){throw Y}}),Y={},O(function(){throw 42},null,De)}catch(n){n!==Y&&(O=null)}else O=null;var vo=/^\s*class\b/,Me=function(e){try{var i=qe.call(e);return vo.test(i)}catch(o){return!1}},Te=function(e){try{return Me(e)?!1:(qe.call(e),!0)}catch(i){return!1}},X=Object.prototype.toString,yo="[object Object]",Co="[object Function]",Po="[object GeneratorFunction]",Do="[object HTMLAllCollection]",To="[object HTML document.all class]",Mo="[object HTMLCollection]",So=typeof Symbol=="function"&&!!Symbol.toStringTag,xo=!(0 in[,]),Se=function(){return!1};typeof document=="object"&&(Qe=document.all,X.call(Qe)===X.call(document.all)&&(Se=function(e){if((xo||!e)&&(typeof e>"u"||typeof e=="object"))try{var i=X.call(e);return(i===Do||i===To||i===Mo||i===yo)&&e("")==null}catch(o){}return!1}));var Qe;function Ao(n){if(Se(n))return!0;if(!n||typeof n!="function"&&typeof n!="object")return!1;try{O(n,null,De)}catch(e){if(e!==Y)return!1}return!Me(n)&&Te(n)}function Ro(n){if(Se(n))return!0;if(!n||typeof n!="function"&&typeof n!="object")return!1;if(So)return Te(n);if(Me(n))return!1;var e=X.call(n);return e!==Co&&e!==Po&&!/^\[object HTML/.test(e)?!1:Te(n)}var W=O?Ao:Ro;var F=class extends Error{constructor(e,i){super(e,i),this.name="CancelError"}},b=class extends Error{constructor(e,i,o){super((o!=null?o:"Unhandled rejection in cancelled promise.")+" Reason: "+Eo(i),{cause:i}),this.promise=e,this.name="CancelledRejectionError"}},h=Symbol("barrier"),xe=Symbol("cancelImpl"),tn,en=(tn=Symbol.species)!=null?tn:Symbol("speciesPolyfill"),H=class n extends Promise{constructor(e,i){let o,t;if(super((c,w)=>{o=c,t=w}),this.constructor[en]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let r={promise:this,resolve:o,reject:t,get oncancelled(){return i!=null?i:null},set oncancelled(c){i=c!=null?c:void 0}},a={get root(){return a},resolving:!1,settled:!1};Object.defineProperties(this,{[h]:{configurable:!1,enumerable:!1,writable:!0,value:null},[xe]:{configurable:!1,enumerable:!1,writable:!1,value:rn(r,a)}});let l=an(r,a);try{e(sn(r,a),l)}catch(c){a.resolving||l(c)}}cancel(e){return new n(i=>{Promise.all([this[xe](new F("Promise cancelled.",{cause:e})),Oo(this)]).then(()=>i(),()=>i())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,i,o){if(!(this instanceof n))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(W(e)||(e=nn),W(i)||(i=on),e===nn&&i==on)return new n(r=>r(this));let t={};return this[h]=t,new n((r,a)=>{super.then(l=>{var c;this[h]===t&&(this[h]=null),(c=t.resolve)==null||c.call(t);try{r(e(l))}catch(w){a(w)}},l=>{var c;this[h]===t&&(this[h]=null),(c=t.resolve)==null||c.call(t);try{r(i(l))}catch(w){a(w)}})},async r=>{try{return o==null?void 0:o(r)}finally{await this.cancel(r)}})}catch(e,i){return this.then(void 0,e,i)}finally(e,i){if(!(this instanceof n))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return W(e)?this.then(o=>n.resolve(e()).then(()=>o),o=>n.resolve(e()).then(()=>{throw o}),i):this.then(e,e,i)}static get[(h,xe,en)](){return Promise}static all(e){let i=Array.from(e),o=i.length===0?n.resolve(i):new n((t,r)=>{Promise.all(i).then(t,r)},t=>_(o,i,t));return o}static allSettled(e){let i=Array.from(e),o=i.length===0?n.resolve(i):new n((t,r)=>{Promise.allSettled(i).then(t,r)},t=>_(o,i,t));return o}static any(e){let i=Array.from(e),o=i.length===0?n.resolve(i):new n((t,r)=>{Promise.any(i).then(t,r)},t=>_(o,i,t));return o}static race(e){let i=Array.from(e),o=new n((t,r)=>{Promise.race(i).then(t,r)},t=>_(o,i,t));return o}static cancel(e){let i=new n(()=>{});return i.cancel(e),i}static timeout(e,i){let o=new n(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void o.cancel(i)):setTimeout(()=>void o.cancel(i),e),o}static sleep(e,i){return new n(o=>{setTimeout(()=>o(i),e)})}static reject(e){return new n((i,o)=>o(e))}static resolve(e){return e instanceof n?e:new n(i=>i(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new n((i,o)=>{e.resolve=i,e.reject=o},i=>{var o;(o=e.oncancelled)==null||o.call(e,i)}),e}};function rn(n,e){let i;return o=>{if(e.settled||(e.settled=!0,e.reason=o,n.reject(o),Promise.prototype.then.call(n.promise,void 0,t=>{if(t!==o)throw t})),!(!e.reason||!n.oncancelled))return i=new Promise(t=>{try{t(n.oncancelled(e.reason.cause))}catch(r){Promise.reject(new b(n.promise,r,"Unhandled exception in oncancelled callback."))}}).catch(t=>{Promise.reject(new b(n.promise,t,"Unhandled rejection in oncancelled callback."))}),n.oncancelled=null,i}}function sn(n,e){return i=>{if(!e.resolving){if(e.resolving=!0,i===n.promise){if(e.settled)return;e.settled=!0,n.reject(new TypeError("A promise cannot be resolved with itself."));return}if(i!=null&&(typeof i=="object"||typeof i=="function")){let o;try{o=i.then}catch(t){e.settled=!0,n.reject(t);return}if(W(o)){try{let a=i.cancel;if(W(a)){let l=c=>{Reflect.apply(a,i,[c])};e.reason?rn(je(Ie({},n),{oncancelled:l}),e)(e.reason):n.oncancelled=l}}catch(a){}let t={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(a){this.root.settled=a},get reason(){return this.root.reason}},r=an(n,t);try{Reflect.apply(o,i,[sn(n,t),r])}catch(a){r(a)}return}}e.settled||(e.settled=!0,n.resolve(i))}}}function an(n,e){return i=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(i instanceof F&&e.reason instanceof F&&Object.is(i.cause,e.reason.cause))return}catch(o){}Promise.reject(new b(n.promise,i))}else e.settled=!0,n.reject(i)}}function _(n,e,i){let o=[];for(let t of e){let r;try{if(!W(t.then)||(r=t.cancel,!W(r)))continue}catch(l){continue}let a;try{a=Reflect.apply(r,t,[i])}catch(l){Promise.reject(new b(n,l,"Unhandled exception in cancel method."));continue}a&&o.push((a instanceof Promise?a:Promise.resolve(a)).catch(l=>{Promise.reject(new b(n,l,"Unhandled rejection in cancel method."))}))}return Promise.all(o)}function nn(n){return n}function on(n){throw n}function Eo(n){try{if(n instanceof Error||typeof n!="object"||n.toString!==Object.prototype.toString)return""+n}catch(e){}try{return JSON.stringify(n)}catch(e){}try{return Object.prototype.toString.call(n)}catch(e){}return""}function Oo(n){var i;let e=(i=n[h])!=null?i:{};return"promise"in e||Object.assign(e,k()),n[h]==null&&(e.resolve(),n[h]=e),e.promise}var k=Promise.withResolvers;k&&typeof k=="function"?k=k.bind(Promise):k=function(){let n,e;return{promise:new Promise((o,t)=>{n=o,e=t}),resolve:n,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Uo;window._wails.callErrorHandler=Io;var ko=m(d.Call),Fo=m(d.CancelCall),L=new Map,Lo=0,zo=0,$=class extends Error{constructor(e,i){super(e,i),this.name="RuntimeError"}};function Uo(n,e,i){let o=ln(n);if(o)if(!e)o.resolve(void 0);else if(!i)o.resolve(e);else try{o.resolve(JSON.parse(e))}catch(t){o.reject(new TypeError("could not parse result: "+t.message,{cause:t}))}}function Io(n,e,i){let o=ln(n);if(o)if(!i)o.reject(new Error(e));else{let t;try{t=JSON.parse(e)}catch(l){o.reject(new TypeError("could not parse error: "+l.message,{cause:l}));return}let r={};t.cause&&(r.cause=t.cause);let a;switch(t.kind){case"ReferenceError":a=new ReferenceError(t.message,r);break;case"TypeError":a=new TypeError(t.message,r);break;case"RuntimeError":a=new $(t.message,r);break;default:a=new Error(t.message,r);break}o.reject(a)}}function ln(n){let e=L.get(n);return L.delete(n),e}function jo(){let n;do n=D();while(L.has(n));return n}function Ae(n){let e=jo(),i=H.withResolvers();L.set(e,{resolve:i.resolve,reject:i.reject});let o=ko(Lo,Object.assign({"call-id":e},n)),t=!1;o.then(()=>{t=!0},a=>{L.delete(e),i.reject(a)});let r=()=>(L.delete(e),Fo(zo,{"call-id":e}).catch(a=>{}));return i.oncancelled=()=>t?r():o.then(r),i.promise}function Bo(n,...e){return Ae({methodName:n,args:e})}function Ho(n,...e){return Ae({methodID:n,args:e})}var Ee={};p(Ee,{SetText:()=>Vo,Text:()=>Go});var cn=m(d.Clipboard),No=0,Zo=1;function Vo(n){return cn(No,{text:n})}function Go(){return cn(Zo)}var Oe={};p(Oe,{Any:()=>P,Array:()=>Yo,ByteSlice:()=>Ko,Map:()=>Xo,Nullable:()=>_o,Struct:()=>$o});function P(n){return n}function Ko(n){return n==null?"":n}function Yo(n){return n===P?e=>e===null?[]:e:e=>{if(e===null)return[];for(let i=0;ii===null?{}:i:i=>{if(i===null)return{};for(let o in i)i[o]=e(i[o]);return i}}function _o(n){return n===P?P:e=>e===null?null:n(e)}function $o(n){let e=!0;for(let i in n)if(n[i]!==P){e=!1;break}return e?P:i=>{for(let o in n)o in i&&(i[o]=n[o](i[o]));return i}}var Fe={};p(Fe,{GetAll:()=>et,GetCurrent:()=>it,GetPrimary:()=>nt});var ke=m(d.Screens),Jo=0,Qo=1,qo=2;function et(){return ke(Jo)}function nt(){return ke(Qo)}function it(){return ke(qo)}window._wails=window._wails||{};window._wails.invoke=y;y("wails:runtime:ready");export{Pe as Application,ee as Browser,Re as Call,F as CancelError,H as CancellablePromise,b as CancelledRejectionError,Ee as Clipboard,Oe as Create,ie as Dialogs,re as Events,ge as Flags,Fe as Screens,fe as System,de as WML,G as Window}; diff --git a/v3/internal/assetserver/bundledassets/runtime_dev.go b/v3/internal/assetserver/bundledassets/runtime_dev.go new file mode 100644 index 000000000..2c9621a66 --- /dev/null +++ b/v3/internal/assetserver/bundledassets/runtime_dev.go @@ -0,0 +1,8 @@ +//go:build !production + +package bundledassets + +import _ "embed" + +//go:embed runtime.debug.js +var RuntimeJS []byte diff --git a/v3/internal/assetserver/bundledassets/runtime_production.go b/v3/internal/assetserver/bundledassets/runtime_production.go new file mode 100644 index 000000000..9614a7b13 --- /dev/null +++ b/v3/internal/assetserver/bundledassets/runtime_production.go @@ -0,0 +1,8 @@ +//go:build production + +package bundledassets + +import _ "embed" + +//go:embed runtime.js +var RuntimeJS []byte diff --git a/v3/internal/assetserver/common.go b/v3/internal/assetserver/common.go new file mode 100644 index 000000000..ce6201b6a --- /dev/null +++ b/v3/internal/assetserver/common.go @@ -0,0 +1,61 @@ +package assetserver + +import ( + "bytes" + "context" + "fmt" + "io" + "log/slog" + "net/http" + "strings" +) + +const ( + HeaderHost = "Host" + HeaderContentType = "Content-Type" + HeaderContentLength = "Content-Length" + HeaderUserAgent = "User-Agent" + // TODO: Is this needed? + HeaderCacheControl = "Cache-Control" + HeaderUpgrade = "Upgrade" + + WailsUserAgentValue = "wails.io" +) + +type assetServerLogger struct{} + +var assetServerLoggerKey assetServerLogger + +func ServeFile(rw http.ResponseWriter, filename string, blob []byte) error { + header := rw.Header() + header.Set(HeaderContentLength, fmt.Sprintf("%d", len(blob))) + if mimeType := header.Get(HeaderContentType); mimeType == "" { + mimeType = GetMimetype(filename, blob) + header.Set(HeaderContentType, mimeType) + } + + rw.WriteHeader(http.StatusOK) + _, err := io.Copy(rw, bytes.NewReader(blob)) + return err +} + +func isWebSocket(req *http.Request) bool { + upgrade := req.Header.Get(HeaderUpgrade) + return strings.EqualFold(upgrade, "websocket") +} + +func contextWithLogger(ctx context.Context, logger *slog.Logger) context.Context { + return context.WithValue(ctx, assetServerLoggerKey, logger) +} + +func logInfo(ctx context.Context, message string, args ...interface{}) { + if logger, _ := ctx.Value(assetServerLoggerKey).(*slog.Logger); logger != nil { + logger.Info(message, args...) + } +} + +func logError(ctx context.Context, message string, args ...interface{}) { + if logger, _ := ctx.Value(assetServerLoggerKey).(*slog.Logger); logger != nil { + logger.Error(message, args...) + } +} diff --git a/v3/internal/assetserver/content_type_sniffer.go b/v3/internal/assetserver/content_type_sniffer.go new file mode 100644 index 000000000..e36db90e8 --- /dev/null +++ b/v3/internal/assetserver/content_type_sniffer.go @@ -0,0 +1,134 @@ +package assetserver + +import ( + "net/http" +) + +func newContentTypeSniffer(rw http.ResponseWriter) *contentTypeSniffer { + return &contentTypeSniffer{ + rw: rw, + closeChannel: make(chan bool, 1), + } +} + +type contentTypeSniffer struct { + rw http.ResponseWriter + prefix []byte + status int + headerCommitted bool + headerWritten bool + closeChannel chan bool +} + +// Unwrap returns the wrapped [http.ResponseWriter] for use with [http.ResponseController]. +func (rw *contentTypeSniffer) Unwrap() http.ResponseWriter { + return rw.rw +} + +func (rw *contentTypeSniffer) Header() http.Header { + return rw.rw.Header() +} + +func (rw *contentTypeSniffer) Write(chunk []byte) (int, error) { + if !rw.headerCommitted { + rw.WriteHeader(http.StatusOK) + } + + if rw.headerWritten { + return rw.rw.Write(chunk) + } + + if len(chunk) == 0 { + return 0, nil + } + + // Cut away at most 512 bytes from chunk, and not less than 0. + cut := max(min(len(chunk), 512-len(rw.prefix)), 0) + if cut >= 512 { + // Avoid copying data if a full prefix is available on first non-zero write. + cut = len(chunk) + rw.prefix = chunk + chunk = nil + } else if cut > 0 { + // First write had less than 512 bytes -- copy data to the prefix buffer. + if rw.prefix == nil { + // Preallocate space for the prefix to be used for sniffing. + rw.prefix = make([]byte, 0, 512) + } + rw.prefix = append(rw.prefix, chunk[:cut]...) + chunk = chunk[cut:] + } + + if len(rw.prefix) < 512 { + return cut, nil + } + + if _, err := rw.complete(); err != nil { + return cut, err + } + + n, err := rw.rw.Write(chunk) + return cut + n, err +} + +func (rw *contentTypeSniffer) WriteHeader(code int) { + if rw.headerCommitted { + return + } + + rw.status = code + rw.headerCommitted = true + + if _, hasType := rw.Header()[HeaderContentType]; hasType { + rw.rw.WriteHeader(rw.status) + rw.headerWritten = true + } +} + +// sniff sniffs the content type from the stored prefix if necessary, +// then writes the header. +func (rw *contentTypeSniffer) sniff() { + if rw.headerWritten || !rw.headerCommitted { + return + } + + m := rw.Header() + if _, hasType := m[HeaderContentType]; !hasType { + m.Set(HeaderContentType, http.DetectContentType(rw.prefix)) + } + + rw.rw.WriteHeader(rw.status) + rw.headerWritten = true +} + +// complete sniffs the content type if necessary, writes the header +// and sends the data prefix that has been stored for sniffing. +// +// Whoever creates a contentTypeSniffer instance +// is responsible for calling complete after the nested handler has returned. +func (rw *contentTypeSniffer) complete() (n int, err error) { + rw.sniff() + + if rw.headerWritten && len(rw.prefix) > 0 { + n, err = rw.rw.Write(rw.prefix) + rw.prefix = nil + } + + return +} + +// CloseNotify implements the http.CloseNotifier interface. +func (rw *contentTypeSniffer) CloseNotify() <-chan bool { + return rw.closeChannel +} + +func (rw *contentTypeSniffer) closeClient() { + rw.closeChannel <- true +} + +// Flush implements the http.Flusher interface. +func (rw *contentTypeSniffer) Flush() { + if f, ok := rw.rw.(http.Flusher); ok { + f.Flush() + } +} diff --git a/v3/internal/assetserver/defaults/index.en.html b/v3/internal/assetserver/defaults/index.en.html new file mode 100644 index 000000000..72c0f567d --- /dev/null +++ b/v3/internal/assetserver/defaults/index.en.html @@ -0,0 +1,350 @@ + + + + + + Page Not Found - Wails + + + +
+ +
+ +
+ + +
+
+
+
⚠️
+ Missing index.html file +
+
+

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

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

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

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

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

+
+
+ + diff --git a/v3/internal/assetserver/fallback_response_writer.go b/v3/internal/assetserver/fallback_response_writer.go new file mode 100644 index 000000000..c26d3cc52 --- /dev/null +++ b/v3/internal/assetserver/fallback_response_writer.go @@ -0,0 +1,80 @@ +package assetserver + +import ( + "maps" + "net/http" +) + +// fallbackResponseWriter wraps a [http.ResponseWriter]. +// If the main handler returns status code 404, +// its response is discarded +// and the request is forwarded to the fallback handler. +type fallbackResponseWriter struct { + rw http.ResponseWriter + req *http.Request + fallback http.Handler + + header http.Header + headerWritten bool + complete bool +} + +// Unwrap returns the wrapped [http.ResponseWriter] for use with [http.ResponseController]. +func (fw *fallbackResponseWriter) Unwrap() http.ResponseWriter { + return fw.rw +} + +func (fw *fallbackResponseWriter) Header() http.Header { + if fw.header == nil { + // Preserve original header in case we get a 404 response. + fw.header = fw.rw.Header().Clone() + } + return fw.header +} + +func (fw *fallbackResponseWriter) Write(chunk []byte) (int, error) { + if fw.complete { + // Fallback triggered, discard further writes. + return len(chunk), nil + } + + if !fw.headerWritten { + fw.WriteHeader(http.StatusOK) + } + + return fw.rw.Write(chunk) +} + +func (fw *fallbackResponseWriter) WriteHeader(statusCode int) { + if fw.headerWritten { + return + } + fw.headerWritten = true + + if statusCode == http.StatusNotFound { + // Protect fallback header from external modifications. + if fw.header == nil { + fw.header = fw.rw.Header().Clone() + } + + // Invoke fallback handler. + fw.complete = true + fw.fallback.ServeHTTP(fw.rw, fw.req) + return + } + + if fw.header != nil { + // Apply headers and forward original map to the main handler. + maps.Copy(fw.rw.Header(), fw.header) + fw.header = fw.rw.Header() + } + + fw.rw.WriteHeader(statusCode) +} + +// Flush implements the http.Flusher interface. +func (rw *fallbackResponseWriter) Flush() { + if f, ok := rw.rw.(http.Flusher); ok { + f.Flush() + } +} diff --git a/v3/internal/assetserver/fs.go b/v3/internal/assetserver/fs.go new file mode 100644 index 000000000..20ccf3fab --- /dev/null +++ b/v3/internal/assetserver/fs.go @@ -0,0 +1,76 @@ +package assetserver + +import ( + "embed" + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" +) + +// findEmbedRootPath finds the root path in the embed FS. It's the directory which contains all the files. +func findEmbedRootPath(fileSystem embed.FS) (string, error) { + stopErr := errors.New("files or multiple dirs found") + + fPath := "" + err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + fPath = path + if entries, dErr := fs.ReadDir(fileSystem, path); dErr != nil { + return dErr + } else if len(entries) <= 1 { + return nil + } + } + + return stopErr + }) + + if err != nil && !errors.Is(err, stopErr) { + return "", err + } + + return fPath, nil +} + +func findPathToFile(fileSystem fs.FS, file string) (string, error) { + stat, _ := fs.Stat(fileSystem, file) + if stat != nil { + return ".", nil + } + var indexFiles []string + err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if strings.HasSuffix(path, file) { + indexFiles = append(indexFiles, path) + } + return nil + }) + if err != nil { + return "", err + } + + if len(indexFiles) > 1 { + selected := indexFiles[0] + for _, f := range indexFiles { + if len(f) < len(selected) { + selected = f + } + } + path, _ := filepath.Split(selected) + return path, nil + } + if len(indexFiles) > 0 { + path, _ := filepath.Split(indexFiles[0]) + return path, nil + } + return "", fmt.Errorf("%s: %w", file, os.ErrNotExist) +} diff --git a/v3/internal/assetserver/middleware.go b/v3/internal/assetserver/middleware.go new file mode 100644 index 000000000..b3826ab7d --- /dev/null +++ b/v3/internal/assetserver/middleware.go @@ -0,0 +1,20 @@ +package assetserver + +import ( + "net/http" +) + +// Middleware defines a HTTP middleware that can be applied to the AssetServer. +// The handler passed as next is the next handler in the chain. One can decide to call the next handler +// or implement a specialized handling. +type Middleware func(next http.Handler) http.Handler + +// ChainMiddleware allows chaining multiple middlewares to one middleware. +func ChainMiddleware(middleware ...Middleware) Middleware { + return func(h http.Handler) http.Handler { + for i := len(middleware) - 1; i >= 0; i-- { + h = middleware[i](h) + } + return h + } +} diff --git a/v3/internal/assetserver/mimecache.go b/v3/internal/assetserver/mimecache.go new file mode 100644 index 000000000..c43de519d --- /dev/null +++ b/v3/internal/assetserver/mimecache.go @@ -0,0 +1,67 @@ +package assetserver + +import ( + "net/http" + "path/filepath" + "sync" + + "github.com/wailsapp/mimetype" +) + +var ( + mimeCache = map[string]string{} + mimeMutex sync.Mutex + + // The list of builtin mime-types by extension as defined by + // the golang standard lib package "mime" + // The standard lib also takes into account mime type definitions from + // /etc files like '/etc/apache2/mime.types' but we want to have the + // same behaviour on all platforms and not depend on some external file. + mimeTypesByExt = map[string]string{ + ".avif": "image/avif", + ".css": "text/css; charset=utf-8", + ".gif": "image/gif", + ".htm": "text/html; charset=utf-8", + ".html": "text/html; charset=utf-8", + ".jpeg": "image/jpeg", + ".jpg": "image/jpeg", + ".js": "text/javascript; charset=utf-8", + ".json": "application/json", + ".mjs": "text/javascript; charset=utf-8", + ".pdf": "application/pdf", + ".png": "image/png", + ".svg": "image/svg+xml", + ".wasm": "application/wasm", + ".webp": "image/webp", + ".xml": "text/xml; charset=utf-8", + } +) + +func GetMimetype(filename string, data []byte) string { + mimeMutex.Lock() + defer mimeMutex.Unlock() + + result := mimeTypesByExt[filepath.Ext(filename)] + if result != "" { + return result + } + + result = mimeCache[filename] + if result != "" { + return result + } + + detect := mimetype.Detect(data) + if detect == nil { + result = http.DetectContentType(data) + } else { + result = detect.String() + } + + if result == "" { + result = "application/octet-stream" + } + + mimeCache[filename] = result + return result +} diff --git a/v3/internal/assetserver/mimecache_test.go b/v3/internal/assetserver/mimecache_test.go new file mode 100644 index 000000000..48e5943fa --- /dev/null +++ b/v3/internal/assetserver/mimecache_test.go @@ -0,0 +1,46 @@ +package assetserver + +import ( + "testing" +) + +func TestGetMimetype(t *testing.T) { + type args struct { + filename string + data []byte + } + bomUTF8 := []byte{0xef, 0xbb, 0xbf} + var emptyMsg []byte + css := []byte("body{margin:0;padding:0;background-color:#d579b2}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;background-color:#ededed}#nav{padding:30px}#nav a{font-weight:700;color:#2c\n3e50}#nav a.router-link-exact-active{color:#42b983}.hello[data-v-4e26ad49]{margin:10px 0}") + html := []byte("title") + bomHtml := append(bomUTF8, html...) + svg := []byte("") + svgWithComment := append([]byte(""), svg...) + svgWithCommentAndControlChars := append([]byte(" \r\n "), svgWithComment...) + svgWithBomCommentAndControlChars := append(bomUTF8, append([]byte(" \r\n "), svgWithComment...)...) + + tests := []struct { + name string + args args + want string + }{ + {"nil data", args{"nil.svg", nil}, "image/svg+xml"}, + {"empty data", args{"empty.html", emptyMsg}, "text/html; charset=utf-8"}, + {"css", args{"test.css", css}, "text/css; charset=utf-8"}, + {"js", args{"test.js", []byte("let foo = 'bar'; console.log(foo);")}, "text/javascript; charset=utf-8"}, + {"mjs", args{"test.mjs", []byte("let foo = 'bar'; console.log(foo);")}, "text/javascript; charset=utf-8"}, + {"html-utf8", args{"test_utf8.html", html}, "text/html; charset=utf-8"}, + {"html-bom-utf8", args{"test_bom_utf8.html", bomHtml}, "text/html; charset=utf-8"}, + {"svg", args{"test.svg", svg}, "image/svg+xml"}, + {"svg-w-comment", args{"test_comment.svg", svgWithComment}, "image/svg+xml"}, + {"svg-w-control-comment", args{"test_control_comment.svg", svgWithCommentAndControlChars}, "image/svg+xml"}, + {"svg-w-bom-control-comment", args{"test_bom_control_comment.svg", svgWithBomCommentAndControlChars}, "image/svg+xml"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetMimetype(tt.args.filename, tt.args.data); got != tt.want { + t.Errorf("GetMimetype() = '%v', want '%v'", got, tt.want) + } + }) + } +} diff --git a/v3/internal/assetserver/options.go b/v3/internal/assetserver/options.go new file mode 100644 index 000000000..ae0570880 --- /dev/null +++ b/v3/internal/assetserver/options.go @@ -0,0 +1,38 @@ +package assetserver + +import ( + "errors" + "log/slog" + "net/http" +) + +// Options defines the configuration of the AssetServer. +type Options struct { + // Handler which serves all the content to the WebView. + Handler http.Handler + + // Middleware is a HTTP Middleware which allows to hook into the AssetServer request chain. It allows to skip the default + // request handler dynamically, e.g. implement specialized Routing etc. + // The Middleware is called to build a new `http.Handler` used by the AssetSever and it also receives the default + // handler used by the AssetServer as an argument. + // + // This middleware injects itself before any of Wails internal middlewares. + // + // If not defined, the default AssetServer request chain is executed. + // + // Multiple Middlewares can be chained together with: + // ChainMiddleware(middleware ...Middleware) Middleware + Middleware Middleware + + // Logger is the logger used by the AssetServer. If not defined, no logging will be done. + Logger *slog.Logger +} + +// Validate the options +func (o Options) Validate() error { + if o.Handler == nil && o.Middleware == nil { + return errors.New("AssetServer options invalid: either Handler or Middleware must be set") + } + + return nil +} diff --git a/v3/internal/assetserver/ringqueue.go b/v3/internal/assetserver/ringqueue.go new file mode 100644 index 000000000..b94e7cd5c --- /dev/null +++ b/v3/internal/assetserver/ringqueue.go @@ -0,0 +1,101 @@ +// Code from https://github.com/erikdubbelboer/ringqueue +/* +The MIT License (MIT) + +Copyright (c) 2015 Erik Dubbelboer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +package assetserver + +type ringqueue[T any] struct { + nodes []T + head int + tail int + cnt int + + minSize int +} + +func newRingqueue[T any](minSize uint) *ringqueue[T] { + if minSize < 2 { + minSize = 2 + } + return &ringqueue[T]{ + nodes: make([]T, minSize), + minSize: int(minSize), + } +} + +func (q *ringqueue[T]) resize(n int) { + nodes := make([]T, n) + if q.head < q.tail { + copy(nodes, q.nodes[q.head:q.tail]) + } else { + copy(nodes, q.nodes[q.head:]) + copy(nodes[len(q.nodes)-q.head:], q.nodes[:q.tail]) + } + + q.tail = q.cnt % n + q.head = 0 + q.nodes = nodes +} + +func (q *ringqueue[T]) Add(i T) { + if q.cnt == len(q.nodes) { + // Also tested a grow rate of 1.5, see: http://stackoverflow.com/questions/2269063/buffer-growth-strategy + // In Go this resulted in a higher memory usage. + q.resize(q.cnt * 2) + } + q.nodes[q.tail] = i + q.tail = (q.tail + 1) % len(q.nodes) + q.cnt++ +} + +func (q *ringqueue[T]) Peek() (T, bool) { + if q.cnt == 0 { + var none T + return none, false + } + return q.nodes[q.head], true +} + +func (q *ringqueue[T]) Remove() (T, bool) { + if q.cnt == 0 { + var none T + return none, false + } + i := q.nodes[q.head] + q.head = (q.head + 1) % len(q.nodes) + q.cnt-- + + if n := len(q.nodes) / 2; n > q.minSize && q.cnt <= n { + q.resize(n) + } + + return i, true +} + +func (q *ringqueue[T]) Cap() int { + return cap(q.nodes) +} + +func (q *ringqueue[T]) Len() int { + return q.cnt +} diff --git a/v3/internal/assetserver/webview/request.go b/v3/internal/assetserver/webview/request.go new file mode 100644 index 000000000..18ff29890 --- /dev/null +++ b/v3/internal/assetserver/webview/request.go @@ -0,0 +1,17 @@ +package webview + +import ( + "io" + "net/http" +) + +type Request interface { + URL() (string, error) + Method() (string, error) + Header() (http.Header, error) + Body() (io.ReadCloser, error) + + Response() ResponseWriter + + Close() error +} diff --git a/v3/internal/assetserver/webview/request_darwin.go b/v3/internal/assetserver/webview/request_darwin.go new file mode 100644 index 000000000..2d8b00168 --- /dev/null +++ b/v3/internal/assetserver/webview/request_darwin.go @@ -0,0 +1,249 @@ +//go:build darwin + +package webview + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework WebKit + +#import +#import +#include + +static void URLSchemeTaskRetain(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + [urlSchemeTask retain]; +} + +static void URLSchemeTaskRelease(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + [urlSchemeTask release]; +} + +static const char * URLSchemeTaskRequestURL(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + @autoreleasepool { + return [urlSchemeTask.request.URL.absoluteString UTF8String]; + } +} + +static const char * URLSchemeTaskRequestMethod(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + @autoreleasepool { + return [urlSchemeTask.request.HTTPMethod UTF8String]; + } +} + +static const char * URLSchemeTaskRequestHeadersJSON(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + @autoreleasepool { + NSData *headerData = [NSJSONSerialization dataWithJSONObject: urlSchemeTask.request.allHTTPHeaderFields options:0 error: nil]; + if (!headerData) { + return nil; + } + + NSString* headerString = [[[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding] autorelease]; + const char * headerJSON = [headerString UTF8String]; + + return strdup(headerJSON); + } +} + +static bool URLSchemeTaskRequestBodyBytes(void *wkUrlSchemeTask, const void **body, int *bodyLen) { + id urlSchemeTask = (id) wkUrlSchemeTask; + @autoreleasepool { + if (!urlSchemeTask.request.HTTPBody) { + return false; + } + + *body = urlSchemeTask.request.HTTPBody.bytes; + *bodyLen = urlSchemeTask.request.HTTPBody.length; + return true; + } +} + +static bool URLSchemeTaskRequestBodyStreamOpen(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + @autoreleasepool { + if (!urlSchemeTask.request.HTTPBodyStream) { + return false; + } + + [urlSchemeTask.request.HTTPBodyStream open]; + return true; + } +} + +static void URLSchemeTaskRequestBodyStreamClose(void *wkUrlSchemeTask) { + id urlSchemeTask = (id) wkUrlSchemeTask; + @autoreleasepool { + if (!urlSchemeTask.request.HTTPBodyStream) { + return; + } + + [urlSchemeTask.request.HTTPBodyStream close]; + } +} + +static int URLSchemeTaskRequestBodyStreamRead(void *wkUrlSchemeTask, void *buf, int bufLen) { + id urlSchemeTask = (id) wkUrlSchemeTask; + + @autoreleasepool { + NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream; + if (!stream) { + return -2; + } + + NSStreamStatus status = stream.streamStatus; + if (status == NSStreamStatusAtEnd || !stream.hasBytesAvailable) { + return 0; + } else if (status != NSStreamStatusOpen) { + return -3; + } + + return [stream read:buf maxLength:bufLen]; + } +} +*/ +import "C" + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "unsafe" +) + +// NewRequest creates as new WebViewRequest based on a pointer to an `id` +func NewRequest(wkURLSchemeTask unsafe.Pointer) Request { + C.URLSchemeTaskRetain(wkURLSchemeTask) + return newRequestFinalizer(&request{task: wkURLSchemeTask}) +} + +var _ Request = &request{} + +type request struct { + task unsafe.Pointer + + header http.Header + body io.ReadCloser + rw *responseWriter +} + +func (r *request) URL() (string, error) { + return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil +} + +func (r *request) Method() (string, error) { + return C.GoString(C.URLSchemeTaskRequestMethod(r.task)), nil +} + +func (r *request) Header() (http.Header, error) { + if r.header != nil { + return r.header, nil + } + + header := http.Header{} + if cHeaders := C.URLSchemeTaskRequestHeadersJSON(r.task); cHeaders != nil { + if headers := C.GoString(cHeaders); headers != "" { + var h map[string]string + if err := json.Unmarshal([]byte(headers), &h); err != nil { + return nil, fmt.Errorf("unable to unmarshal request headers: %s", err) + } + + for k, v := range h { + header.Add(k, v) + } + } + C.free(unsafe.Pointer(cHeaders)) + } + r.header = header + return header, nil +} + +func (r *request) Body() (io.ReadCloser, error) { + if r.body != nil { + return r.body, nil + } + + var body unsafe.Pointer + var bodyLen C.int + if C.URLSchemeTaskRequestBodyBytes(r.task, &body, &bodyLen) { + if body != nil && bodyLen > 0 { + r.body = io.NopCloser(bytes.NewReader(C.GoBytes(body, bodyLen))) + } else { + r.body = http.NoBody + } + } else if C.URLSchemeTaskRequestBodyStreamOpen(r.task) { + r.body = &requestBodyStreamReader{task: r.task} + } + + return r.body, nil +} + +func (r *request) Response() ResponseWriter { + if r.rw != nil { + return r.rw + } + + r.rw = &responseWriter{r: r} + return r.rw +} + +func (r *request) Close() error { + var err error + if r.body != nil { + err = r.body.Close() + } + r.Response().Finish() + C.URLSchemeTaskRelease(r.task) + return err +} + +var _ io.ReadCloser = &requestBodyStreamReader{} + +type requestBodyStreamReader struct { + task unsafe.Pointer + closed bool +} + +// Read implements io.Reader +func (r *requestBodyStreamReader) Read(p []byte) (n int, err error) { + var content unsafe.Pointer + var contentLen int + if p != nil { + content = unsafe.Pointer(&p[0]) + contentLen = len(p) + } + + res := C.URLSchemeTaskRequestBodyStreamRead(r.task, content, C.int(contentLen)) + if res > 0 { + return int(res), nil + } + + switch res { + case 0: + return 0, io.EOF + case -1: + return 0, errors.New("body: stream error") + case -2: + return 0, errors.New("body: no stream defined") + case -3: + return 0, io.ErrClosedPipe + default: + return 0, fmt.Errorf("body: unknown error %d", res) + } +} + +func (r *requestBodyStreamReader) Close() error { + if r.closed { + return nil + } + r.closed = true + + C.URLSchemeTaskRequestBodyStreamClose(r.task) + return nil +} diff --git a/v3/internal/assetserver/webview/request_finalizer.go b/v3/internal/assetserver/webview/request_finalizer.go new file mode 100644 index 000000000..6a8c6a928 --- /dev/null +++ b/v3/internal/assetserver/webview/request_finalizer.go @@ -0,0 +1,40 @@ +package webview + +import ( + "runtime" + "sync/atomic" +) + +var _ Request = &requestFinalizer{} + +type requestFinalizer struct { + Request + closed int32 +} + +// newRequestFinalizer returns a request with a runtime finalizer to make sure it will be closed from the finalizer +// if it has not been already closed. +// It also makes sure Close() of the wrapping request is only called once. +func newRequestFinalizer(r Request) Request { + rf := &requestFinalizer{Request: r} + // Make sure to async release since it might block the finalizer goroutine for a longer period + runtime.SetFinalizer(rf, func(obj *requestFinalizer) { rf.close(true) }) + return rf +} + +func (r *requestFinalizer) Close() error { + return r.close(false) +} + +func (r *requestFinalizer) close(asyncRelease bool) error { + if atomic.CompareAndSwapInt32(&r.closed, 0, 1) { + runtime.SetFinalizer(r, nil) + if asyncRelease { + go r.Request.Close() + return nil + } else { + return r.Request.Close() + } + } + return nil +} diff --git a/v3/internal/assetserver/webview/request_linux.go b/v3/internal/assetserver/webview/request_linux.go new file mode 100644 index 000000000..32969a1ba --- /dev/null +++ b/v3/internal/assetserver/webview/request_linux.go @@ -0,0 +1,83 @@ +//go:build linux +// +build linux + +package webview + +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 gio-unix-2.0 + +#include "gtk/gtk.h" +#include "webkit2/webkit2.h" +*/ +import "C" + +import ( + "io" + "net/http" + "unsafe" +) + +// NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest` +func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request { + webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest) + C.g_object_ref(C.gpointer(webkitReq)) + + req := &request{req: webkitReq} + return newRequestFinalizer(req) +} + +var _ Request = &request{} + +type request struct { + req *C.WebKitURISchemeRequest + + header http.Header + body io.ReadCloser + rw *responseWriter +} + +func (r *request) URL() (string, error) { + return C.GoString(C.webkit_uri_scheme_request_get_uri(r.req)), nil +} + +func (r *request) Method() (string, error) { + return webkit_uri_scheme_request_get_http_method(r.req), nil +} + +func (r *request) Header() (http.Header, error) { + if r.header != nil { + return r.header, nil + } + + r.header = webkit_uri_scheme_request_get_http_headers(r.req) + return r.header, nil +} + +func (r *request) Body() (io.ReadCloser, error) { + if r.body != nil { + return r.body, nil + } + + r.body = webkit_uri_scheme_request_get_http_body(r.req) + + return r.body, nil +} + +func (r *request) Response() ResponseWriter { + if r.rw != nil { + return r.rw + } + + r.rw = &responseWriter{req: r.req} + return r.rw +} + +func (r *request) Close() error { + var err error + if r.body != nil { + err = r.body.Close() + } + r.Response().Finish() + C.g_object_unref(C.gpointer(r.req)) + return err +} diff --git a/v3/internal/assetserver/webview/request_linux_purego.go b/v3/internal/assetserver/webview/request_linux_purego.go new file mode 100644 index 000000000..bf724a55b --- /dev/null +++ b/v3/internal/assetserver/webview/request_linux_purego.go @@ -0,0 +1,94 @@ +//go:build linux && purego +// +build linux,purego + +package webview + +import ( + "io" + "net/http" + + "github.com/ebitengine/purego" +) + +// NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest` +// +// Please make sure to call Release() when finished using the request. +func NewRequest(webKitURISchemeRequest uintptr) Request { + webkitReq := webKitURISchemeRequest + req := &request{req: webkitReq} + req.AddRef() + return req +} + +var _ Request = &request{} + +type request struct { + req uintptr + + header http.Header + body io.ReadCloser + rw *responseWriter +} + +func (r *request) AddRef() error { + var objectRef func(uintptr) + purego.RegisterLibFunc(&objectRef, gtk, "g_object_ref") + objectRef(r.req) + return nil +} + +func (r *request) Release() error { + var objectUnref func(uintptr) + purego.RegisterLibFunc(&objectUnref, gtk, "g_object_unref") + objectUnref(r.req) + return nil +} + +func (r *request) URL() (string, error) { + var getUri func(uintptr) string + purego.RegisterLibFunc(&getUri, webkit, "webkit_uri_scheme_request_get_uri") + return getUri(r.req), nil +} + +func (r *request) Method() (string, error) { + return webkit_uri_scheme_request_get_http_method(r.req), nil +} + +func (r *request) Header() (http.Header, error) { + if r.header != nil { + return r.header, nil + } + + r.header = webkit_uri_scheme_request_get_http_headers(r.req) + return r.header, nil +} + +func (r *request) Body() (io.ReadCloser, error) { + if r.body != nil { + return r.body, nil + } + + // WebKit2GTK has currently no support for request bodies. + r.body = http.NoBody + + return r.body, nil +} + +func (r *request) Response() ResponseWriter { + if r.rw != nil { + return r.rw + } + + r.rw = &responseWriter{req: r.req} + return r.rw +} + +func (r *request) Close() error { + var err error + if r.body != nil { + err = r.body.Close() + } + r.Response().Finish() + r.Release() + return err +} diff --git a/v3/internal/assetserver/webview/request_windows.go b/v3/internal/assetserver/webview/request_windows.go new file mode 100644 index 000000000..9f68af2e1 --- /dev/null +++ b/v3/internal/assetserver/webview/request_windows.go @@ -0,0 +1,218 @@ +//go:build windows + +package webview + +import ( + "errors" + "fmt" + "io" + "net/http" + + "github.com/wailsapp/go-webview2/pkg/edge" +) + +// NewRequest creates as new WebViewRequest for chromium. This Method must be called from the Main-Thread! +func NewRequest(env *edge.ICoreWebView2Environment, args *edge.ICoreWebView2WebResourceRequestedEventArgs, invokeSync func(fn func())) (Request, error) { + req, err := args.GetRequest() + if err != nil { + return nil, fmt.Errorf("GetRequest failed: %s", err) + } + defer req.Release() + + r := &request{ + invokeSync: invokeSync, + } + + code := http.StatusInternalServerError + r.response, err = env.CreateWebResourceResponse(nil, code, http.StatusText(code), "") + if err != nil { + return nil, fmt.Errorf("CreateWebResourceResponse failed: %s", err) + } + + if err := args.PutResponse(r.response); err != nil { + r.finishResponse() + return nil, fmt.Errorf("PutResponse failed: %s", err) + } + + r.deferral, err = args.GetDeferral() + if err != nil { + r.finishResponse() + return nil, fmt.Errorf("GetDeferral failed: %s", err) + } + + r.url, r.urlErr = req.GetUri() + r.method, r.methodErr = req.GetMethod() + r.header, r.headerErr = getHeaders(req) + + if content, err := req.GetContent(); err != nil { + r.bodyErr = err + } else if content != nil { + // It is safe to access Content from another Thread: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#thread-safety + r.body = &iStreamReleaseCloser{stream: content} + } + + return r, nil +} + +var _ Request = &request{} + +type request struct { + response *edge.ICoreWebView2WebResourceResponse + deferral *edge.ICoreWebView2Deferral + + url string + urlErr error + + method string + methodErr error + + header http.Header + headerErr error + + body io.ReadCloser + bodyErr error + rw *responseWriter + + invokeSync func(fn func()) +} + +func (r *request) URL() (string, error) { + return r.url, r.urlErr +} + +func (r *request) Method() (string, error) { + return r.method, r.methodErr +} + +func (r *request) Header() (http.Header, error) { + return r.header, r.headerErr +} + +func (r *request) Body() (io.ReadCloser, error) { + return r.body, r.bodyErr +} + +func (r *request) Response() ResponseWriter { + if r.rw != nil { + return r.rw + } + + r.rw = &responseWriter{req: r} + return r.rw +} + +func (r *request) Close() error { + var errs []error + if r.body != nil { + if err := r.body.Close(); err != nil { + errs = append(errs, err) + } + r.body = nil + } + + if err := r.Response().Finish(); err != nil { + errs = append(errs, err) + } + + return combineErrs(errs) +} + +// finishResponse must be called on the main-thread +func (r *request) finishResponse() error { + var errs []error + if r.response != nil { + if err := r.response.Release(); err != nil { + errs = append(errs, err) + } + r.response = nil + } + if r.deferral != nil { + if err := r.deferral.Complete(); err != nil { + errs = append(errs, err) + } + + if err := r.deferral.Release(); err != nil { + errs = append(errs, err) + } + r.deferral = nil + } + return combineErrs(errs) +} + +type iStreamReleaseCloser struct { + stream *edge.IStream + closed bool +} + +func (i *iStreamReleaseCloser) Read(p []byte) (int, error) { + if i.closed { + return 0, io.ErrClosedPipe + } + return i.stream.Read(p) +} + +func (i *iStreamReleaseCloser) Close() error { + if i.closed { + return nil + } + i.closed = true + return i.stream.Release() +} + +func getHeaders(req *edge.ICoreWebView2WebResourceRequest) (http.Header, error) { + header := http.Header{} + headers, err := req.GetHeaders() + if err != nil { + return nil, fmt.Errorf("GetHeaders Error: %s", err) + } + defer headers.Release() + + headersIt, err := headers.GetIterator() + if err != nil { + return nil, fmt.Errorf("GetIterator Error: %s", err) + } + defer headersIt.Release() + + for { + has, err := headersIt.HasCurrentHeader() + if err != nil { + return nil, fmt.Errorf("HasCurrentHeader Error: %s", err) + } + if !has { + break + } + + name, value, err := headersIt.GetCurrentHeader() + if err != nil { + return nil, fmt.Errorf("GetCurrentHeader Error: %s", err) + } + + header.Set(name, value) + if _, err := headersIt.MoveNext(); err != nil { + return nil, fmt.Errorf("MoveNext Error: %s", err) + } + } + + // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other + // requests including IPC calls. + // So prevent 304 status codes by removing the headers that are used in combinationwith caching. + header.Del("If-Modified-Since") + header.Del("If-None-Match") + return header, nil +} + +func combineErrs(errs []error) error { + err := errors.Join(errs...) + + if err != nil { + // errors.Join wraps even a single error. + // Check the filtered error list, + // and if it has just one element return it directly. + errs = err.(interface{ Unwrap() []error }).Unwrap() + if len(errs) == 1 { + return errs[0] + } + } + + return err +} diff --git a/v3/internal/assetserver/webview/responsewriter.go b/v3/internal/assetserver/webview/responsewriter.go new file mode 100644 index 000000000..2fc7ede51 --- /dev/null +++ b/v3/internal/assetserver/webview/responsewriter.go @@ -0,0 +1,28 @@ +package webview + +import ( + "errors" + "net/http" +) + +const ( + HeaderContentLength = "Content-Length" + HeaderContentType = "Content-Type" +) + +var ( + errRequestStopped = errors.New("request has been stopped") + errResponseFinished = errors.New("response has been finished") +) + +// A ResponseWriter interface is used by an HTTP handler to +// construct an HTTP response for the WebView. +type ResponseWriter interface { + http.ResponseWriter + + // Finish the response and flush all data. A Finish after the request has already been finished has no effect. + Finish() error + + // Code returns the HTTP status code of the response + Code() int +} diff --git a/v3/internal/assetserver/webview/responsewriter_darwin.go b/v3/internal/assetserver/webview/responsewriter_darwin.go new file mode 100644 index 000000000..499ae6f9f --- /dev/null +++ b/v3/internal/assetserver/webview/responsewriter_darwin.go @@ -0,0 +1,155 @@ +//go:build darwin + +package webview + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework WebKit + +#import +#import + +typedef void (^schemeTaskCaller)(id); + +static bool urlSchemeTaskCall(void *wkUrlSchemeTask, schemeTaskCaller fn) { + id urlSchemeTask = (id) wkUrlSchemeTask; + if (urlSchemeTask == nil) { + return false; + } + + @autoreleasepool { + @try { + fn(urlSchemeTask); + } @catch (NSException *exception) { + // This is very bad to detect a stopped schemeTask this should be implemented in a better way + // But it seems to be very tricky to not deadlock when keeping a lock curing executing fn() + // It seems like those call switch the thread back to the main thread and then deadlocks when they reentrant want + // to get the lock again to start another request or stop it. + if ([exception.reason isEqualToString: @"This task has already been stopped"]) { + return false; + } + + @throw exception; + } + + return true; + } +} + +static bool URLSchemeTaskDidReceiveData(void *wkUrlSchemeTask, void* data, int datalength) { + return urlSchemeTaskCall( + wkUrlSchemeTask, + ^(id urlSchemeTask) { + NSData *nsdata = [NSData dataWithBytes:data length:datalength]; + [urlSchemeTask didReceiveData:nsdata]; + }); +} + +static bool URLSchemeTaskDidFinish(void *wkUrlSchemeTask) { + return urlSchemeTaskCall( + wkUrlSchemeTask, + ^(id urlSchemeTask) { + [urlSchemeTask didFinish]; + }); +} + +static bool URLSchemeTaskDidReceiveResponse(void *wkUrlSchemeTask, int statusCode, void *headersString, int headersStringLength) { + return urlSchemeTaskCall( + wkUrlSchemeTask, + ^(id urlSchemeTask) { + NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength]; + NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData:nsHeadersJSON options: NSJSONReadingMutableContainers error: nil]; + NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease]; + + [urlSchemeTask didReceiveResponse:response]; + }); +} +*/ +import "C" + +import ( + "encoding/json" + "net/http" + "unsafe" +) + +var _ ResponseWriter = &responseWriter{} + +type responseWriter struct { + r *request + + header http.Header + wroteHeader bool + code int + + finished bool +} + +func (rw *responseWriter) Header() http.Header { + if rw.header == nil { + rw.header = http.Header{} + } + return rw.header +} + +func (rw *responseWriter) Write(buf []byte) (int, error) { + if rw.finished { + return 0, errResponseFinished + } + + rw.WriteHeader(http.StatusOK) + + var content unsafe.Pointer + var contentLen int + if buf != nil { + content = unsafe.Pointer(&buf[0]) + contentLen = len(buf) + } + + if !C.URLSchemeTaskDidReceiveData(rw.r.task, content, C.int(contentLen)) { + return 0, errRequestStopped + } + return contentLen, nil +} + +func (rw *responseWriter) WriteHeader(code int) { + rw.code = code + if rw.wroteHeader || rw.finished { + return + } + rw.wroteHeader = true + + header := map[string]string{} + for k := range rw.Header() { + header[k] = rw.Header().Get(k) + } + headerData, _ := json.Marshal(header) + + var headers unsafe.Pointer + var headersLen int + if len(headerData) != 0 { + headers = unsafe.Pointer(&headerData[0]) + headersLen = len(headerData) + } + + C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen)) +} + +func (rw *responseWriter) Finish() error { + if !rw.wroteHeader { + rw.WriteHeader(http.StatusNotImplemented) + } + + if rw.finished { + return nil + } + rw.finished = true + + C.URLSchemeTaskDidFinish(rw.r.task) + + return nil +} + +func (rw *responseWriter) Code() int { + return rw.code +} diff --git a/v3/internal/assetserver/webview/responsewriter_linux.go b/v3/internal/assetserver/webview/responsewriter_linux.go new file mode 100644 index 000000000..169b68ab5 --- /dev/null +++ b/v3/internal/assetserver/webview/responsewriter_linux.go @@ -0,0 +1,135 @@ +//go:build linux + +package webview + +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 gio-unix-2.0 + +#include "gtk/gtk.h" +#include "webkit2/webkit2.h" +#include "gio/gunixinputstream.h" + +*/ +import "C" +import ( + "fmt" + "io" + "net/http" + "os" + "strconv" + "syscall" + "unsafe" +) + +type responseWriter struct { + req *C.WebKitURISchemeRequest + + header http.Header + wroteHeader bool + finished bool + code int + + w io.WriteCloser + wErr error +} + +func (rw *responseWriter) Code() int { + return rw.code +} + +func (rw *responseWriter) Header() http.Header { + if rw.header == nil { + rw.header = http.Header{} + } + return rw.header +} + +func (rw *responseWriter) Write(buf []byte) (int, error) { + if rw.finished { + return 0, errResponseFinished + } + + rw.WriteHeader(http.StatusOK) + if rw.wErr != nil { + return 0, rw.wErr + } + return rw.w.Write(buf) +} + +func (rw *responseWriter) WriteHeader(code int) { + rw.code = code + if rw.wroteHeader || rw.finished { + return + } + rw.wroteHeader = true + + contentLength := int64(-1) + if sLen := rw.Header().Get(HeaderContentLength); sLen != "" { + if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 { + contentLength = pLen + } + } + + // We can't use os.Pipe here, because that returns files with a finalizer for closing the FD. But the control over the + // read FD is given to the InputStream and will be closed there. + // Furthermore we especially don't want to have the FD_CLOEXEC + rFD, w, err := pipe() + if err != nil { + rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to open pipe: %s", err)) + return + } + rw.w = w + + stream := C.g_unix_input_stream_new(C.int(rFD), C.gboolean(1)) + defer C.g_object_unref(C.gpointer(stream)) + + if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), stream, contentLength); err != nil { + rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err)) + return + } +} + +func (rw *responseWriter) Finish() error { + if !rw.wroteHeader { + rw.WriteHeader(http.StatusNotImplemented) + } + + if rw.finished { + return nil + } + rw.finished = true + if rw.w != nil { + rw.w.Close() + } + return nil +} + +func (rw *responseWriter) finishWithError(code int, err error) { + if rw.w != nil { + rw.w.Close() + rw.w = &nopCloser{io.Discard} + } + rw.wErr = err + + msg := C.CString(err.Error()) + gerr := C.g_error_new_literal(C.g_quark_from_string(msg), C.int(code), msg) + C.webkit_uri_scheme_request_finish_error(rw.req, gerr) + C.g_error_free(gerr) + C.free(unsafe.Pointer(msg)) +} + +type nopCloser struct { + io.Writer +} + +func (nopCloser) Close() error { return nil } + +func pipe() (r int, w *os.File, err error) { + var p [2]int + e := syscall.Pipe2(p[0:], 0) + if e != nil { + return 0, nil, fmt.Errorf("pipe2: %s", e) + } + + return p[0], os.NewFile(uintptr(p[1]), "|1"), nil +} diff --git a/v3/internal/assetserver/webview/responsewriter_linux_purego.go b/v3/internal/assetserver/webview/responsewriter_linux_purego.go new file mode 100644 index 000000000..6742d1bda --- /dev/null +++ b/v3/internal/assetserver/webview/responsewriter_linux_purego.go @@ -0,0 +1,180 @@ +//go:build linux && purego +// +build linux,purego + +package webview + +import ( + "fmt" + "io" + "net/http" + "os" + "strconv" + "syscall" + + "github.com/ebitengine/purego" +) + +const ( + gtk3 = "libgtk-3.so" + gtk4 = "libgtk-4.so" +) + +var ( + gtk uintptr + webkit uintptr + version int +) + +func init() { + var err error + // gtk, err = purego.Dlopen(gtk4, purego.RTLD_NOW|purego.RTLD_GLOBAL) + // if err == nil { + // version = 4 + // return + // } + // log.Println("Failed to open GTK4: Falling back to GTK3") + gtk, err = purego.Dlopen(gtk3, purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } + version = 3 + + var webkit4 string = "libwebkit2gtk-4.1.so" + webkit, err = purego.Dlopen(webkit4, purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } +} + +type responseWriter struct { + req uintptr + + header http.Header + wroteHeader bool + finished bool + code int + w io.WriteCloser + wErr error +} + +func (rw *responseWriter) Code() int { + return rw.code +} + +func (rw *responseWriter) Header() http.Header { + if rw.header == nil { + rw.header = http.Header{} + } + return rw.header +} + +func (rw *responseWriter) Write(buf []byte) (int, error) { + if rw.finished { + return 0, errResponseFinished + } + + rw.WriteHeader(http.StatusOK) + if rw.wErr != nil { + return 0, rw.wErr + } + return rw.w.Write(buf) +} + +func (rw *responseWriter) WriteHeader(code int) { + rw.code = code + + // TODO? Is this ever called? I don't think so! + if rw.wroteHeader || rw.finished { + return + } + rw.wroteHeader = true + + contentLength := int64(-1) + if sLen := rw.Header().Get(HeaderContentLength); sLen != "" { + if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 { + contentLength = pLen + } + } + // We can't use os.Pipe here, because that returns files with a finalizer for closing the FD. But the control over the + // read FD is given to the InputStream and will be closed there. + // Furthermore we especially don't want to have the FD_CLOEXEC + rFD, w, err := pipe() + if err != nil { + rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to open pipe: %s", err)) + return + } + rw.w = w + + var newStream func(int, bool) uintptr + purego.RegisterLibFunc(&newStream, gtk, "g_unix_input_stream_new") + var unRef func(uintptr) + purego.RegisterLibFunc(&unRef, gtk, "g_object_unref") + stream := newStream(rFD, true) + + /* var reqFinish func(uintptr, uintptr, uintptr, uintptr, int64) int + purego.RegisterLibFunc(&reqFinish, webkit, "webkit_uri_scheme_request_finish") + + header := rw.Header() + defer unRef(stream) + if err := reqFinish(rw.req, code, header, stream, contentLength); err != nil { + rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err)) + } + */ + if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), stream, contentLength); err != nil { + rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err)) + return + } +} + +func (rw *responseWriter) Finish() { + if !rw.wroteHeader { + rw.WriteHeader(http.StatusNotImplemented) + } + + if rw.finished { + return + } + rw.finished = true + if rw.w != nil { + rw.w.Close() + } +} + +func (rw *responseWriter) finishWithError(code int, err error) { + if rw.w != nil { + rw.w.Close() + rw.w = &nopCloser{io.Discard} + } + rw.wErr = err + + var newLiteral func(uint32, string, int, string) uintptr // is this correct? + purego.RegisterLibFunc(&newLiteral, gtk, "g_error_new_literal") + var newQuark func(string) uintptr + purego.RegisterLibFunc(&newQuark, gtk, "g_quark_from_string") + var freeError func(uintptr) + purego.RegisterLibFunc(&freeError, gtk, "g_error_free") + var finishError func(uintptr, uintptr) + purego.RegisterLibFunc(&finishError, webkit, "webkit_uri_scheme_request_finish_error") + + msg := string(err.Error()) + //gquark := newQuark(msg) + gerr := newLiteral(1, msg, code, msg) + finishError(rw.req, gerr) + freeError(gerr) +} + +type nopCloser struct { + io.Writer +} + +func (nopCloser) Close() error { return nil } + +func pipe() (r int, w *os.File, err error) { + var p [2]int + e := syscall.Pipe2(p[0:], 0) + if e != nil { + return 0, nil, fmt.Errorf("pipe2: %s", e) + } + + return p[0], os.NewFile(uintptr(p[1]), "|1"), nil +} diff --git a/v3/internal/assetserver/webview/responsewriter_windows.go b/v3/internal/assetserver/webview/responsewriter_windows.go new file mode 100644 index 000000000..c003f00bd --- /dev/null +++ b/v3/internal/assetserver/webview/responsewriter_windows.go @@ -0,0 +1,109 @@ +//go:build windows + +package webview + +import ( + "bytes" + "errors" + "fmt" + "net/http" + "strings" +) + +var _ http.ResponseWriter = &responseWriter{} + +type responseWriter struct { + req *request + + header http.Header + wroteHeader bool + code int + body *bytes.Buffer + + finished bool +} + +func (rw *responseWriter) Header() http.Header { + if rw.header == nil { + rw.header = http.Header{} + } + return rw.header +} + +func (rw *responseWriter) Write(buf []byte) (int, error) { + if rw.finished { + return 0, errResponseFinished + } + + rw.WriteHeader(http.StatusOK) + + return rw.body.Write(buf) +} + +func (rw *responseWriter) WriteHeader(code int) { + if rw.wroteHeader || rw.finished { + return + } + rw.wroteHeader = true + + if rw.body == nil { + rw.body = &bytes.Buffer{} + } + + rw.code = code +} + +func (rw *responseWriter) Finish() error { + if !rw.wroteHeader { + rw.WriteHeader(http.StatusNotImplemented) + } + + if rw.finished { + return nil + } + rw.finished = true + + var errs []error + + code := rw.code + if code == http.StatusNotModified { + // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other + // requests including IPC calls. + errs = append(errs, errors.New("AssetServer returned 304 - StatusNotModified which are going to hang WebView2, changed code to 505 - StatusInternalServerError")) + code = http.StatusInternalServerError + } + + rw.req.invokeSync(func() { + resp := rw.req.response + + hdrs, err := resp.GetHeaders() + if err != nil { + errs = append(errs, fmt.Errorf("Resp.GetHeaders failed: %s", err)) + } else { + for k, v := range rw.header { + if err := hdrs.AppendHeader(k, strings.Join(v, ",")); err != nil { + errs = append(errs, fmt.Errorf("Resp.AppendHeader failed: %s", err)) + } + } + hdrs.Release() + } + + if err := resp.PutStatusCode(code); err != nil { + errs = append(errs, fmt.Errorf("Resp.PutStatusCode failed: %s", err)) + } + + if err := resp.PutByteContent(rw.body.Bytes()); err != nil { + errs = append(errs, fmt.Errorf("Resp.PutByteContent failed: %s", err)) + } + + if err := rw.req.finishResponse(); err != nil { + errs = append(errs, fmt.Errorf("Resp.finishResponse failed: %s", err)) + } + }) + + return combineErrs(errs) +} + +func (rw *responseWriter) Code() int { + return rw.code +} diff --git a/v3/internal/assetserver/webview/webkit2.go b/v3/internal/assetserver/webview/webkit2.go new file mode 100644 index 000000000..21156a4c5 --- /dev/null +++ b/v3/internal/assetserver/webview/webkit2.go @@ -0,0 +1,137 @@ +//go:build linux + +package webview + +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 libsoup-3.0 + +#include "gtk/gtk.h" +#include "webkit2/webkit2.h" +#include "libsoup/soup.h" +*/ +import "C" + +import ( + "fmt" + "io" + "net/http" + "strings" + "unsafe" +) + +const Webkit2MinMinorVersion = 40 + +func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string { + method := C.GoString(C.webkit_uri_scheme_request_get_http_method(req)) + return strings.ToUpper(method) +} + +func webkit_uri_scheme_request_get_http_headers(req *C.WebKitURISchemeRequest) http.Header { + hdrs := C.webkit_uri_scheme_request_get_http_headers(req) + + var iter C.SoupMessageHeadersIter + C.soup_message_headers_iter_init(&iter, hdrs) + + var name *C.char + var value *C.char + + h := http.Header{} + for C.soup_message_headers_iter_next(&iter, &name, &value) != 0 { + h.Add(C.GoString(name), C.GoString(value)) + } + + return h +} + +func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, header http.Header, stream *C.GInputStream, streamLength int64) error { + resp := C.webkit_uri_scheme_response_new(stream, C.gint64(streamLength)) + defer C.g_object_unref(C.gpointer(resp)) + + cReason := C.CString(http.StatusText(code)) + C.webkit_uri_scheme_response_set_status(resp, C.guint(code), cReason) + C.free(unsafe.Pointer(cReason)) + + cMimeType := C.CString(header.Get(HeaderContentType)) + C.webkit_uri_scheme_response_set_content_type(resp, cMimeType) + C.free(unsafe.Pointer(cMimeType)) + + hdrs := C.soup_message_headers_new(C.SOUP_MESSAGE_HEADERS_RESPONSE) + for name, values := range header { + cName := C.CString(name) + for _, value := range values { + cValue := C.CString(value) + C.soup_message_headers_append(hdrs, cName, cValue) + C.free(unsafe.Pointer(cValue)) + } + C.free(unsafe.Pointer(cName)) + } + + C.webkit_uri_scheme_response_set_http_headers(resp, hdrs) + + C.webkit_uri_scheme_request_finish_with_response(req, resp) + return nil +} + +func webkit_uri_scheme_request_get_http_body(req *C.WebKitURISchemeRequest) io.ReadCloser { + stream := C.webkit_uri_scheme_request_get_http_body(req) + if stream == nil { + return http.NoBody + } + return &webkitRequestBody{stream: stream} +} + +type webkitRequestBody struct { + stream *C.GInputStream + closed bool +} + +// Read implements io.Reader +func (r *webkitRequestBody) Read(p []byte) (int, error) { + if r.closed { + return 0, io.ErrClosedPipe + } + + var content unsafe.Pointer + var contentLen int + if p != nil { + content = unsafe.Pointer(&p[0]) + contentLen = len(p) + } + + var n C.gsize + var gErr *C.GError + res := C.g_input_stream_read_all(r.stream, content, C.gsize(contentLen), &n, nil, &gErr) + if res == 0 { + return 0, formatGError("stream read failed", gErr) + } else if n == 0 { + return 0, io.EOF + } + return int(n), nil +} + +func (r *webkitRequestBody) Close() error { + if r.closed { + return nil + } + r.closed = true + + // https://docs.gtk.org/gio/method.InputStream.close.html + // Streams will be automatically closed when the last reference is dropped, but you might want to call this function + // to make sure resources are released as early as possible. + var err error + var gErr *C.GError + if C.g_input_stream_close(r.stream, nil, &gErr) == 0 { + err = formatGError("stream close failed", gErr) + } + C.g_object_unref(C.gpointer(r.stream)) + r.stream = nil + return err +} + +func formatGError(msg string, gErr *C.GError, args ...any) error { + if gErr != nil && gErr.message != nil { + msg += ": " + C.GoString(gErr.message) + C.g_error_free(gErr) + } + return fmt.Errorf(msg, args...) +} diff --git a/v3/internal/assetserver/webview/webkit2_purego.go b/v3/internal/assetserver/webview/webkit2_purego.go new file mode 100644 index 000000000..28832c8bd --- /dev/null +++ b/v3/internal/assetserver/webview/webkit2_purego.go @@ -0,0 +1,158 @@ +//go:build linux && purego + +package webview + +import ( + "net/http" + "strings" + + "github.com/ebitengine/purego" +) + +func webkit_uri_scheme_request_get_http_method(req uintptr) string { + var getMethod func(uintptr) string + purego.RegisterLibFunc(&getMethod, gtk, "webkit_uri_scheme_request_get_http_method") + return strings.ToUpper(getMethod(req)) +} + +func webkit_uri_scheme_request_get_http_headers(req uintptr) http.Header { + var getHeaders func(uintptr) uintptr + purego.RegisterLibFunc(&getUri, webkit, "webkit_uri_scheme_request_get_http_headers") + + hdrs := getHeaders(req) + + var headersIterInit func(uintptr, uintptr) uintptr + purego.RegisterLibFunc(&headersIterInit, gtk, "soup_message_headers_iter_init") + + // TODO: How do we get a struct? + /* + typedef struct { + SoupMessageHeaders *hdrs; + int index_common; + int index_uncommon; + } SoupMessageHeadersIterReal; + */ + iter := make([]byte, 12) + headersIterInit(&iter, hdrs) + + var iterNext func(uintptr, *string, *string) int + purego.RegisterLibFunc(&iterNext, gtk, "soup_message_headers_iter_next") + + var name string + var value string + h := http.Header{} + + for iterNext(&iter, &name, &value) != 0 { + h.Add(name, value) + } + + return h +} + +func webkit_uri_scheme_request_finish(req uintptr, code int, header http.Header, stream uintptr, streamLength int64) error { + + var newResponse func(uintptr, int64) string + purego.RegisterLibFunc(&newResponse, webkit, "webkit_uri_scheme_response_new") + var unRef func(uintptr) + purego.RegisterLibFunc(&unRef, gtk, "g_object_unref") + + resp := newResponse(stream, streamLength) + defer unRef(resp) + + var setStatus func(uintptr, int, string) + purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_response_set_status") + + setStatus(resp, code, cReason) + + var setContentType func(uintptr, string) + purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_response_set_content_type") + + setContentType(resp, header.Get(HeaderContentType)) + + soup := gtk + var soupHeadersNew func(int) uintptr + purego.RegisterLibFunc(&unRef, soup, "soup_message_headers_new") + var soupHeadersAppend func(uintptr, string, string) + purego.RegisterLibFunc(&unRef, soup, "soup_message_headers_append") + + hdrs := soupHeadersNew(SOUP_MESSAGE_HEADERS_RESPONSE) + for name, values := range header { + for _, value := range values { + soupHeadersAppend(hdrs, name, value) + } + } + + var setHttpHeaders func(uintptr, uintptr) + purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_response_set_http_headers") + + setHttpHeaders(resp, hdrs) + var finishWithResponse func(uintptr, uintptr) + purego.RegisterLibFunc(&unRef, webkit, "webkit_uri_scheme_request_finish_with_response") + finishWithResponse(req, resp) + + return nil +} + +func webkit_uri_scheme_request_get_http_body(req *C.WebKitURISchemeRequest) io.ReadCloser { + stream := C.webkit_uri_scheme_request_get_http_body(req) + if stream == nil { + return http.NoBody + } + return &webkitRequestBody{stream: stream} +} + +type webkitRequestBody struct { + stream *C.GInputStream + closed bool +} + +// Read implements io.Reader +func (r *webkitRequestBody) Read(p []byte) (int, error) { + if r.closed { + return 0, io.ErrClosedPipe + } + + var content unsafe.Pointer + var contentLen int + if p != nil { + content = unsafe.Pointer(&p[0]) + contentLen = len(p) + } + + var n C.gsize + var gErr *C.GError + res := C.g_input_stream_read_all(r.stream, content, C.gsize(contentLen), &n, nil, &gErr) + if res == 0 { + return 0, formatGError("stream read failed", gErr) + } else if n == 0 { + return 0, io.EOF + } + return int(n), nil +} + +func (r *webkitRequestBody) Close() error { + if r.closed { + return nil + } + r.closed = true + + // https://docs.gtk.org/gio/method.InputStream.close.html + // Streams will be automatically closed when the last reference is dropped, but you might want to call this function + // to make sure resources are released as early as possible. + var err error + var gErr *C.GError + if C.g_input_stream_close(r.stream, nil, &gErr) == 0 { + err = formatGError("stream close failed", gErr) + } + C.g_object_unref(C.gpointer(r.stream)) + r.stream = nil + return err +} + +func formatGError(msg string, gErr *C.GError, args ...any) error { + if gErr != nil && gErr.message != nil { + msg += ": " + C.GoString(gErr.message) + C.g_error_free(gErr) + } + return fmt.Errorf(msg, args...) +} diff --git a/v3/internal/buildinfo/buildinfo.go b/v3/internal/buildinfo/buildinfo.go new file mode 100644 index 000000000..930831f1f --- /dev/null +++ b/v3/internal/buildinfo/buildinfo.go @@ -0,0 +1,40 @@ +package buildinfo + +import ( + "fmt" + "runtime/debug" + "slices" + + "github.com/samber/lo" +) + +type Info struct { + Development bool + Version string + BuildSettings map[string]string + wailsPackage *debug.Module +} + +func Get() (*Info, error) { + + var result Info + + // BuildInfo contains the build info for the application + var BuildInfo *debug.BuildInfo + + var ok bool + BuildInfo, ok = debug.ReadBuildInfo() + if !ok { + return nil, fmt.Errorf("could not read build info from binary") + } + result.BuildSettings = lo.Associate(BuildInfo.Settings, func(setting debug.BuildSetting) (string, string) { + return setting.Key, setting.Value + }) + result.Version = BuildInfo.Main.Version + result.Development = -1 != slices.IndexFunc(BuildInfo.Settings, func(setting debug.BuildSetting) bool { + return setting.Key == "vcs" && setting.Value == "git" + }) + + return &result, nil + +} diff --git a/v3/internal/buildinfo/buildinfo_test.go b/v3/internal/buildinfo/buildinfo_test.go new file mode 100644 index 000000000..ab79f63f8 --- /dev/null +++ b/v3/internal/buildinfo/buildinfo_test.go @@ -0,0 +1,13 @@ +package buildinfo + +import ( + "testing" +) + +func TestGet(t *testing.T) { + result, err := Get() + if err != nil { + t.Error(err) + } + _ = result +} diff --git a/v3/internal/capabilities/capabilities.go b/v3/internal/capabilities/capabilities.go new file mode 100644 index 000000000..af9428bb2 --- /dev/null +++ b/v3/internal/capabilities/capabilities.go @@ -0,0 +1,16 @@ +package capabilities + +import "encoding/json" + +type Capabilities struct { + HasNativeDrag bool +} + +func (c Capabilities) AsBytes() []byte { + // JSON encode + result, err := json.Marshal(c) + if err != nil { + return []byte("{}") + } + return result +} diff --git a/v3/internal/capabilities/capabilities_darwin.go b/v3/internal/capabilities/capabilities_darwin.go new file mode 100644 index 000000000..5cd0b600c --- /dev/null +++ b/v3/internal/capabilities/capabilities_darwin.go @@ -0,0 +1,9 @@ +//go:build darwin + +package capabilities + +func newCapabilities(_ string) Capabilities { + c := Capabilities{} + c.HasNativeDrag = false + return c +} diff --git a/v3/internal/capabilities/capabilities_linux.go b/v3/internal/capabilities/capabilities_linux.go new file mode 100644 index 000000000..b0debdbb0 --- /dev/null +++ b/v3/internal/capabilities/capabilities_linux.go @@ -0,0 +1,11 @@ +//go:build linux + +package capabilities + +func NewCapabilities() Capabilities { + c := Capabilities{} + // For now, assume Linux has native drag support + // TODO: Implement proper WebKit version detection + c.HasNativeDrag = true + return c +} diff --git a/v3/internal/capabilities/capabilities_windows.go b/v3/internal/capabilities/capabilities_windows.go new file mode 100644 index 000000000..78591dcf6 --- /dev/null +++ b/v3/internal/capabilities/capabilities_windows.go @@ -0,0 +1,22 @@ +//go:build windows + +package capabilities + +import "github.com/wailsapp/go-webview2/webviewloader" + +type version string + +func (v version) IsAtLeast(input string) bool { + result, err := webviewloader.CompareBrowserVersions(string(v), input) + if err != nil { + return false + } + return result >= 0 +} + +func NewCapabilities(webview2version string) Capabilities { + webview2 := version(webview2version) + c := Capabilities{} + c.HasNativeDrag = webview2.IsAtLeast("113.0.0.0") + return c +} diff --git a/v3/internal/changelog/changelog.go b/v3/internal/changelog/changelog.go new file mode 100644 index 000000000..2690ea674 --- /dev/null +++ b/v3/internal/changelog/changelog.go @@ -0,0 +1,80 @@ +package changelog + +import ( + "fmt" + "io" + "os" + "strings" +) + +// ProcessorResult contains the results of processing a changelog file +type ProcessorResult struct { + Entry *ChangelogEntry + ValidationResult ValidationResult + HasContent bool +} + +// Processor handles the complete changelog processing pipeline +type Processor struct { + parser *Parser + validator *Validator +} + +// NewProcessor creates a new changelog processor with parser and validator +func NewProcessor() *Processor { + return &Processor{ + parser: NewParser(), + validator: NewValidator(), + } +} + +// ProcessFile processes a changelog file and returns the parsed and validated entry +func (p *Processor) ProcessFile(filePath string) (*ProcessorResult, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, fmt.Errorf("failed to open changelog file %s: %w", filePath, err) + } + defer file.Close() + + return p.ProcessReader(file) +} + +// ProcessReader processes changelog content from a reader +func (p *Processor) ProcessReader(reader io.Reader) (*ProcessorResult, error) { + // Parse the content + entry, err := p.parser.ParseContent(reader) + if err != nil { + return nil, fmt.Errorf("failed to parse changelog content: %w", err) + } + + // Validate the parsed entry + validationResult := p.validator.ValidateEntry(entry) + + result := &ProcessorResult{ + Entry: entry, + ValidationResult: validationResult, + HasContent: entry.HasContent(), + } + + return result, nil +} + +// ProcessString processes changelog content from a string +func (p *Processor) ProcessString(content string) (*ProcessorResult, error) { + return p.ProcessReader(strings.NewReader(content)) +} + +// ValidateFile validates a changelog file without parsing it into an entry +func (p *Processor) ValidateFile(filePath string) (ValidationResult, error) { + content, err := os.ReadFile(filePath) + if err != nil { + return ValidationResult{Valid: false}, fmt.Errorf("failed to read changelog file %s: %w", filePath, err) + } + + return p.validator.ValidateContent(string(content)), nil +} + +// ValidateString validates changelog content from a string +func (p *Processor) ValidateString(content string) ValidationResult { + return p.validator.ValidateContent(content) +} diff --git a/v3/internal/changelog/changelog_test.go b/v3/internal/changelog/changelog_test.go new file mode 100644 index 000000000..88a2ddbd0 --- /dev/null +++ b/v3/internal/changelog/changelog_test.go @@ -0,0 +1,296 @@ +package changelog + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestNewProcessor(t *testing.T) { + processor := NewProcessor() + if processor == nil { + t.Fatal("NewProcessor() returned nil") + } + if processor.parser == nil { + t.Error("parser not initialized") + } + if processor.validator == nil { + t.Error("validator not initialized") + } +} + +func TestProcessString_ValidContent(t *testing.T) { + processor := NewProcessor() + content := `# Unreleased Changes + +## Added +- Add support for custom window icons in application options +- Add new SetWindowIcon() method to runtime API (#1234) + +## Fixed +- Fix memory leak in event system during window close operations (#5678)` + + result, err := processor.ProcessString(content) + if err != nil { + t.Fatalf("ProcessString() returned error: %v", err) + } + + if !result.HasContent { + t.Error("Result should have content") + } + + if !result.ValidationResult.Valid { + t.Errorf("Validation should pass, got errors: %s", result.ValidationResult.GetValidationSummary()) + } + + // Check parsed content + if len(result.Entry.Added) != 2 { + t.Errorf("Expected 2 Added items, got %d", len(result.Entry.Added)) + } + if len(result.Entry.Fixed) != 1 { + t.Errorf("Expected 1 Fixed item, got %d", len(result.Entry.Fixed)) + } +} + +func TestProcessString_InvalidContent(t *testing.T) { + processor := NewProcessor() + content := `# Unreleased Changes + +## Added +- Short +- TODO: add proper description + +## InvalidSection +- This section is invalid` + + result, err := processor.ProcessString(content) + if err != nil { + t.Fatalf("ProcessString() returned error: %v", err) + } + + if result.ValidationResult.Valid { + t.Error("Validation should fail for invalid content") + } + + // The parser will parse the Added section correctly (2 items) + // The InvalidSection won't be parsed since it's not a valid section name + if len(result.Entry.Added) != 2 { + t.Errorf("Expected 2 Added items, got %d", len(result.Entry.Added)) + } + + // Should have validation errors + if len(result.ValidationResult.Errors) == 0 { + t.Error("Should have validation errors") + } +} + +func TestProcessString_EmptyContent(t *testing.T) { + processor := NewProcessor() + content := `# Unreleased Changes + +## Added + + +## Changed +` + + result, err := processor.ProcessString(content) + if err != nil { + t.Fatalf("ProcessString() returned error: %v", err) + } + + if result.HasContent { + t.Error("Result should not have content") + } + + if result.ValidationResult.Valid { + t.Error("Validation should fail for empty content") + } +} + +func TestProcessFile_ValidFile(t *testing.T) { + processor := NewProcessor() + + // Create a temporary file + tmpDir := t.TempDir() + filePath := filepath.Join(tmpDir, "test_changelog.md") + + content := `# Unreleased Changes + +## Added +- Add support for custom window icons in application options +- Add new SetWindowIcon() method to runtime API (#1234) + +## Fixed +- Fix memory leak in event system during window close operations (#5678)` + + err := os.WriteFile(filePath, []byte(content), 0644) + if err != nil { + t.Fatalf("Failed to create test file: %v", err) + } + + result, err := processor.ProcessFile(filePath) + if err != nil { + t.Fatalf("ProcessFile() returned error: %v", err) + } + + if !result.HasContent { + t.Error("Result should have content") + } + + if !result.ValidationResult.Valid { + t.Errorf("Validation should pass, got errors: %s", result.ValidationResult.GetValidationSummary()) + } + + // Check parsed content + if len(result.Entry.Added) != 2 { + t.Errorf("Expected 2 Added items, got %d", len(result.Entry.Added)) + } + if len(result.Entry.Fixed) != 1 { + t.Errorf("Expected 1 Fixed item, got %d", len(result.Entry.Fixed)) + } +} + +func TestProcessFile_NonexistentFile(t *testing.T) { + processor := NewProcessor() + + result, err := processor.ProcessFile("/nonexistent/file.md") + if err == nil { + t.Error("ProcessFile() should return error for nonexistent file") + } + if result != nil { + t.Error("ProcessFile() should return nil result for nonexistent file") + } +} + +func TestValidateString_ValidContent(t *testing.T) { + processor := NewProcessor() + content := `# Unreleased Changes + +## Added +- Add support for custom window icons in application options + +## Fixed +- Fix memory leak in event system during window close operations` + + result := processor.ValidateString(content) + if !result.Valid { + t.Errorf("ValidateString() should be valid, got errors: %s", result.GetValidationSummary()) + } +} + +func TestValidateString_InvalidContent(t *testing.T) { + processor := NewProcessor() + content := `# Unreleased Changes + +## InvalidSection +- This section is invalid + +- Bullet point outside section` + + result := processor.ValidateString(content) + if result.Valid { + t.Error("ValidateString() should be invalid") + } + + if len(result.Errors) == 0 { + t.Error("ValidateString() should return validation errors") + } +} + +func TestValidateFile_ValidFile(t *testing.T) { + processor := NewProcessor() + + // Create a temporary file + tmpDir := t.TempDir() + filePath := filepath.Join(tmpDir, "test_changelog.md") + + content := `# Unreleased Changes + +## Added +- Add support for custom window icons in application options + +## Fixed +- Fix memory leak in event system during window close operations` + + err := os.WriteFile(filePath, []byte(content), 0644) + if err != nil { + t.Fatalf("Failed to create test file: %v", err) + } + + result, err := processor.ValidateFile(filePath) + if err != nil { + t.Fatalf("ValidateFile() returned error: %v", err) + } + + if !result.Valid { + t.Errorf("ValidateFile() should be valid, got errors: %s", result.GetValidationSummary()) + } +} + +func TestValidateFile_NonexistentFile(t *testing.T) { + processor := NewProcessor() + + result, err := processor.ValidateFile("/nonexistent/file.md") + if err == nil { + t.Error("ValidateFile() should return error for nonexistent file") + } + if result.Valid { + t.Error("ValidateFile() should return invalid result for nonexistent file") + } +} + +func TestProcessorResult_Integration(t *testing.T) { + processor := NewProcessor() + content := `# Unreleased Changes + +## Added +- Add comprehensive changelog processing system +- Add validation for Keep a Changelog format compliance + +## Changed +- Update changelog workflow to use automated processing + +## Fixed +- Fix parsing issues with various markdown bullet styles +- Fix validation edge cases for empty content sections` + + result, err := processor.ProcessString(content) + if err != nil { + t.Fatalf("ProcessString() returned error: %v", err) + } + + // Test that we can format the result for different outputs + changelogFormat := result.Entry.FormatForChangelog() + if !strings.Contains(changelogFormat, "### Added") { + t.Error("FormatForChangelog() should contain Added section") + } + if !strings.Contains(changelogFormat, "### Changed") { + t.Error("FormatForChangelog() should contain Changed section") + } + if !strings.Contains(changelogFormat, "### Fixed") { + t.Error("FormatForChangelog() should contain Fixed section") + } + + releaseFormat := result.Entry.FormatForRelease() + if !strings.Contains(releaseFormat, "## ✨ Added") { + t.Error("FormatForRelease() should contain Added section with emoji") + } + if !strings.Contains(releaseFormat, "## 🔄 Changed") { + t.Error("FormatForRelease() should contain Changed section with emoji") + } + if !strings.Contains(releaseFormat, "## 🐛 Fixed") { + t.Error("FormatForRelease() should contain Fixed section with emoji") + } + + // Test validation summary + if !result.ValidationResult.Valid { + t.Errorf("Validation should pass, got: %s", result.ValidationResult.GetValidationSummary()) + } + + summary := result.ValidationResult.GetValidationSummary() + if !strings.Contains(summary, "Validation passed") { + t.Errorf("Validation summary should indicate success, got: %s", summary) + } +} diff --git a/v3/internal/changelog/parser.go b/v3/internal/changelog/parser.go new file mode 100644 index 000000000..3557e26a0 --- /dev/null +++ b/v3/internal/changelog/parser.go @@ -0,0 +1,239 @@ +package changelog + +import ( + "bufio" + "fmt" + "io" + "regexp" + "strings" + "time" +) + +// ChangelogEntry represents a parsed changelog entry following Keep a Changelog format +type ChangelogEntry struct { + Version string `json:"version"` + Date time.Time `json:"date"` + Added []string `json:"added"` + Changed []string `json:"changed"` + Fixed []string `json:"fixed"` + Deprecated []string `json:"deprecated"` + Removed []string `json:"removed"` + Security []string `json:"security"` +} + +// Parser handles parsing of UNRELEASED_CHANGELOG.md files +type Parser struct { + // sectionRegex matches section headers like "## Added", "## Changed", etc. + sectionRegex *regexp.Regexp + // bulletRegex matches bullet points (- or *) + bulletRegex *regexp.Regexp +} + +// NewParser creates a new changelog parser +func NewParser() *Parser { + return &Parser{ + sectionRegex: regexp.MustCompile(`^##\s+(Added|Changed|Fixed|Deprecated|Removed|Security)\s*$`), + bulletRegex: regexp.MustCompile(`^[\s]*[-*]\s+(.+)$`), + } +} + +// ParseContent parses changelog content from a reader and returns a ChangelogEntry +func (p *Parser) ParseContent(reader io.Reader) (*ChangelogEntry, error) { + entry := &ChangelogEntry{ + Added: []string{}, + Changed: []string{}, + Fixed: []string{}, + Deprecated: []string{}, + Removed: []string{}, + Security: []string{}, + } + + scanner := bufio.NewScanner(reader) + var currentSection string + var inExampleSection bool + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + // Skip empty lines and comments + if line == "" || strings.HasPrefix(line, "") { + continue + } + + // Skip the main title + if strings.HasPrefix(line, "# Unreleased Changes") { + continue + } + + // Check if we're entering the example section + if strings.HasPrefix(line, "---") || strings.HasPrefix(line, "### Example Entries") { + inExampleSection = true + continue + } + + // Skip example section content + if inExampleSection { + continue + } + + // Check for section headers + if strings.HasPrefix(line, "##") { + if matches := p.sectionRegex.FindStringSubmatch(line); len(matches) > 1 { + currentSection = strings.ToLower(matches[1]) + } else { + // Invalid section header - reset current section + currentSection = "" + } + continue + } + + // Parse bullet points + if matches := p.bulletRegex.FindStringSubmatch(line); len(matches) > 1 { + content := strings.TrimSpace(matches[1]) + if content == "" { + continue + } + + switch currentSection { + case "added": + entry.Added = append(entry.Added, content) + case "changed": + entry.Changed = append(entry.Changed, content) + case "fixed": + entry.Fixed = append(entry.Fixed, content) + case "deprecated": + entry.Deprecated = append(entry.Deprecated, content) + case "removed": + entry.Removed = append(entry.Removed, content) + case "security": + entry.Security = append(entry.Security, content) + } + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading changelog content: %w", err) + } + + return entry, nil +} + +// HasContent checks if the changelog entry contains any actual content +func (entry *ChangelogEntry) HasContent() bool { + return len(entry.Added) > 0 || + len(entry.Changed) > 0 || + len(entry.Fixed) > 0 || + len(entry.Deprecated) > 0 || + len(entry.Removed) > 0 || + len(entry.Security) > 0 +} + +// FormatForChangelog formats the entry for insertion into the main changelog +func (entry *ChangelogEntry) FormatForChangelog() string { + var builder strings.Builder + + if len(entry.Added) > 0 { + builder.WriteString("### Added\n") + for _, item := range entry.Added { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Changed) > 0 { + builder.WriteString("### Changed\n") + for _, item := range entry.Changed { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Fixed) > 0 { + builder.WriteString("### Fixed\n") + for _, item := range entry.Fixed { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Deprecated) > 0 { + builder.WriteString("### Deprecated\n") + for _, item := range entry.Deprecated { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Removed) > 0 { + builder.WriteString("### Removed\n") + for _, item := range entry.Removed { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Security) > 0 { + builder.WriteString("### Security\n") + for _, item := range entry.Security { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + return strings.TrimSpace(builder.String()) +} + +// FormatForRelease formats the entry for GitHub release notes +func (entry *ChangelogEntry) FormatForRelease() string { + var builder strings.Builder + + if len(entry.Added) > 0 { + builder.WriteString("## ✨ Added\n") + for _, item := range entry.Added { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Changed) > 0 { + builder.WriteString("## 🔄 Changed\n") + for _, item := range entry.Changed { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Fixed) > 0 { + builder.WriteString("## 🐛 Fixed\n") + for _, item := range entry.Fixed { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Deprecated) > 0 { + builder.WriteString("## ⚠️ Deprecated\n") + for _, item := range entry.Deprecated { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Removed) > 0 { + builder.WriteString("## 🗑️ Removed\n") + for _, item := range entry.Removed { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + if len(entry.Security) > 0 { + builder.WriteString("## 🔒 Security\n") + for _, item := range entry.Security { + builder.WriteString(fmt.Sprintf("- %s\n", item)) + } + builder.WriteString("\n") + } + + return strings.TrimSpace(builder.String()) +} diff --git a/v3/internal/changelog/parser_test.go b/v3/internal/changelog/parser_test.go new file mode 100644 index 000000000..fbdb35c8c --- /dev/null +++ b/v3/internal/changelog/parser_test.go @@ -0,0 +1,468 @@ +package changelog + +import ( + "strings" + "testing" +) + +func TestNewParser(t *testing.T) { + parser := NewParser() + if parser == nil { + t.Fatal("NewParser() returned nil") + } + if parser.sectionRegex == nil { + t.Error("sectionRegex not initialized") + } + if parser.bulletRegex == nil { + t.Error("bulletRegex not initialized") + } +} + +func TestParseContent_EmptyContent(t *testing.T) { + parser := NewParser() + reader := strings.NewReader("") + + entry, err := parser.ParseContent(reader) + if err != nil { + t.Fatalf("ParseContent() returned error: %v", err) + } + + if entry.HasContent() { + t.Error("Empty content should not have content") + } +} + +func TestParseContent_OnlyComments(t *testing.T) { + parser := NewParser() + content := `# Unreleased Changes + + + + +## Added + + +## Changed +` + + reader := strings.NewReader(content) + entry, err := parser.ParseContent(reader) + if err != nil { + t.Fatalf("ParseContent() returned error: %v", err) + } + + if entry.HasContent() { + t.Error("Content with only comments should not have content") + } +} + +func TestParseContent_BasicSections(t *testing.T) { + parser := NewParser() + content := `# Unreleased Changes + +## Added +- New feature A +- New feature B + +## Changed +- Changed feature C + +## Fixed +- Fixed bug D +- Fixed bug E + +## Deprecated +- Deprecated feature F + +## Removed +- Removed feature G + +## Security +- Security fix H` + + reader := strings.NewReader(content) + entry, err := parser.ParseContent(reader) + if err != nil { + t.Fatalf("ParseContent() returned error: %v", err) + } + + // Test Added section + if len(entry.Added) != 2 { + t.Errorf("Expected 2 Added items, got %d", len(entry.Added)) + } + if entry.Added[0] != "New feature A" { + t.Errorf("Expected 'New feature A', got '%s'", entry.Added[0]) + } + if entry.Added[1] != "New feature B" { + t.Errorf("Expected 'New feature B', got '%s'", entry.Added[1]) + } + + // Test Changed section + if len(entry.Changed) != 1 { + t.Errorf("Expected 1 Changed item, got %d", len(entry.Changed)) + } + if entry.Changed[0] != "Changed feature C" { + t.Errorf("Expected 'Changed feature C', got '%s'", entry.Changed[0]) + } + + // Test Fixed section + if len(entry.Fixed) != 2 { + t.Errorf("Expected 2 Fixed items, got %d", len(entry.Fixed)) + } + if entry.Fixed[0] != "Fixed bug D" { + t.Errorf("Expected 'Fixed bug D', got '%s'", entry.Fixed[0]) + } + if entry.Fixed[1] != "Fixed bug E" { + t.Errorf("Expected 'Fixed bug E', got '%s'", entry.Fixed[1]) + } + + // Test Deprecated section + if len(entry.Deprecated) != 1 { + t.Errorf("Expected 1 Deprecated item, got %d", len(entry.Deprecated)) + } + if entry.Deprecated[0] != "Deprecated feature F" { + t.Errorf("Expected 'Deprecated feature F', got '%s'", entry.Deprecated[0]) + } + + // Test Removed section + if len(entry.Removed) != 1 { + t.Errorf("Expected 1 Removed item, got %d", len(entry.Removed)) + } + if entry.Removed[0] != "Removed feature G" { + t.Errorf("Expected 'Removed feature G', got '%s'", entry.Removed[0]) + } + + // Test Security section + if len(entry.Security) != 1 { + t.Errorf("Expected 1 Security item, got %d", len(entry.Security)) + } + if entry.Security[0] != "Security fix H" { + t.Errorf("Expected 'Security fix H', got '%s'", entry.Security[0]) + } + + // Test HasContent + if !entry.HasContent() { + t.Error("Entry should have content") + } +} + +func TestParseContent_WithExampleSection(t *testing.T) { + parser := NewParser() + content := `# Unreleased Changes + +## Added +- Real feature A + +## Changed +- Real change B + +--- + +### Example Entries: + +**Added:** +- Example feature that should be ignored +- Another example that should be ignored + +**Fixed:** +- Example fix that should be ignored` + + reader := strings.NewReader(content) + entry, err := parser.ParseContent(reader) + if err != nil { + t.Fatalf("ParseContent() returned error: %v", err) + } + + // Should only have the real entries, not the examples + if len(entry.Added) != 1 { + t.Errorf("Expected 1 Added item, got %d", len(entry.Added)) + } + if entry.Added[0] != "Real feature A" { + t.Errorf("Expected 'Real feature A', got '%s'", entry.Added[0]) + } + + if len(entry.Changed) != 1 { + t.Errorf("Expected 1 Changed item, got %d", len(entry.Changed)) + } + if entry.Changed[0] != "Real change B" { + t.Errorf("Expected 'Real change B', got '%s'", entry.Changed[0]) + } + + // Should not have any Fixed items from examples + if len(entry.Fixed) != 0 { + t.Errorf("Expected 0 Fixed items, got %d", len(entry.Fixed)) + } +} + +func TestParseContent_DifferentBulletStyles(t *testing.T) { + parser := NewParser() + content := `# Unreleased Changes + +## Added +- Feature with dash +* Feature with asterisk + - Indented feature with dash + * Indented feature with asterisk + +## Fixed +- Feature with extra spaces +* Another with extra spaces` + + reader := strings.NewReader(content) + entry, err := parser.ParseContent(reader) + if err != nil { + t.Fatalf("ParseContent() returned error: %v", err) + } + + expectedAdded := []string{ + "Feature with dash", + "Feature with asterisk", + "Indented feature with dash", + "Indented feature with asterisk", + } + + if len(entry.Added) != len(expectedAdded) { + t.Errorf("Expected %d Added items, got %d", len(expectedAdded), len(entry.Added)) + } + + for i, expected := range expectedAdded { + if i >= len(entry.Added) || entry.Added[i] != expected { + t.Errorf("Expected Added[%d] to be '%s', got '%s'", i, expected, entry.Added[i]) + } + } + + expectedFixed := []string{ + "Feature with extra spaces", + "Another with extra spaces", + } + + if len(entry.Fixed) != len(expectedFixed) { + t.Errorf("Expected %d Fixed items, got %d", len(expectedFixed), len(entry.Fixed)) + } + + for i, expected := range expectedFixed { + if i >= len(entry.Fixed) || entry.Fixed[i] != expected { + t.Errorf("Expected Fixed[%d] to be '%s', got '%s'", i, expected, entry.Fixed[i]) + } + } +} + +func TestParseContent_EmptyBulletPoints(t *testing.T) { + parser := NewParser() + content := `# Unreleased Changes + +## Added +- Valid feature +- +- +- Another valid feature + +## Fixed +- +- Valid fix` + + reader := strings.NewReader(content) + entry, err := parser.ParseContent(reader) + if err != nil { + t.Fatalf("ParseContent() returned error: %v", err) + } + + // Should skip empty bullet points + expectedAdded := []string{ + "Valid feature", + "Another valid feature", + } + + if len(entry.Added) != len(expectedAdded) { + t.Errorf("Expected %d Added items, got %d", len(expectedAdded), len(entry.Added)) + } + + for i, expected := range expectedAdded { + if i >= len(entry.Added) || entry.Added[i] != expected { + t.Errorf("Expected Added[%d] to be '%s', got '%s'", i, expected, entry.Added[i]) + } + } + + expectedFixed := []string{"Valid fix"} + if len(entry.Fixed) != len(expectedFixed) { + t.Errorf("Expected %d Fixed items, got %d", len(expectedFixed), len(entry.Fixed)) + } + if entry.Fixed[0] != "Valid fix" { + t.Errorf("Expected 'Valid fix', got '%s'", entry.Fixed[0]) + } +} + +func TestHasContent(t *testing.T) { + tests := []struct { + name string + entry ChangelogEntry + expected bool + }{ + { + name: "Empty entry", + entry: ChangelogEntry{}, + expected: false, + }, + { + name: "Entry with Added items", + entry: ChangelogEntry{ + Added: []string{"Feature A"}, + }, + expected: true, + }, + { + name: "Entry with Changed items", + entry: ChangelogEntry{ + Changed: []string{"Change A"}, + }, + expected: true, + }, + { + name: "Entry with Fixed items", + entry: ChangelogEntry{ + Fixed: []string{"Fix A"}, + }, + expected: true, + }, + { + name: "Entry with Deprecated items", + entry: ChangelogEntry{ + Deprecated: []string{"Deprecated A"}, + }, + expected: true, + }, + { + name: "Entry with Removed items", + entry: ChangelogEntry{ + Removed: []string{"Removed A"}, + }, + expected: true, + }, + { + name: "Entry with Security items", + entry: ChangelogEntry{ + Security: []string{"Security A"}, + }, + expected: true, + }, + { + name: "Entry with multiple sections", + entry: ChangelogEntry{ + Added: []string{"Feature A"}, + Fixed: []string{"Fix A"}, + Changed: []string{"Change A"}, + }, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.entry.HasContent(); got != tt.expected { + t.Errorf("HasContent() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestFormatForChangelog(t *testing.T) { + entry := ChangelogEntry{ + Added: []string{"New feature A", "New feature B"}, + Changed: []string{"Changed feature C"}, + Fixed: []string{"Fixed bug D"}, + Deprecated: []string{"Deprecated feature E"}, + Removed: []string{"Removed feature F"}, + Security: []string{"Security fix G"}, + } + + result := entry.FormatForChangelog() + + expected := `### Added +- New feature A +- New feature B + +### Changed +- Changed feature C + +### Fixed +- Fixed bug D + +### Deprecated +- Deprecated feature E + +### Removed +- Removed feature F + +### Security +- Security fix G` + + if result != expected { + t.Errorf("FormatForChangelog() mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, result) + } +} + +func TestFormatForChangelog_PartialSections(t *testing.T) { + entry := ChangelogEntry{ + Added: []string{"New feature A"}, + Fixed: []string{"Fixed bug B"}, + // Other sections empty + } + + result := entry.FormatForChangelog() + + expected := `### Added +- New feature A + +### Fixed +- Fixed bug B` + + if result != expected { + t.Errorf("FormatForChangelog() mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, result) + } +} + +func TestFormatForRelease(t *testing.T) { + entry := ChangelogEntry{ + Added: []string{"New feature A", "New feature B"}, + Changed: []string{"Changed feature C"}, + Fixed: []string{"Fixed bug D"}, + Deprecated: []string{"Deprecated feature E"}, + Removed: []string{"Removed feature F"}, + Security: []string{"Security fix G"}, + } + + result := entry.FormatForRelease() + + expected := `## ✨ Added +- New feature A +- New feature B + +## 🔄 Changed +- Changed feature C + +## 🐛 Fixed +- Fixed bug D + +## ⚠️ Deprecated +- Deprecated feature E + +## 🗑️ Removed +- Removed feature F + +## 🔒 Security +- Security fix G` + + if result != expected { + t.Errorf("FormatForRelease() mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, result) + } +} + +func TestFormatForRelease_EmptyEntry(t *testing.T) { + entry := ChangelogEntry{} + + result := entry.FormatForRelease() + + if result != "" { + t.Errorf("FormatForRelease() for empty entry should return empty string, got: %s", result) + } +} diff --git a/v3/internal/changelog/validator.go b/v3/internal/changelog/validator.go new file mode 100644 index 000000000..20bff9ac3 --- /dev/null +++ b/v3/internal/changelog/validator.go @@ -0,0 +1,316 @@ +package changelog + +import ( + "fmt" + "regexp" + "strings" +) + +// ValidationError represents a validation error with context +type ValidationError struct { + Field string + Message string + Line int +} + +func (e ValidationError) Error() string { + if e.Line > 0 { + return fmt.Sprintf("validation error at line %d in %s: %s", e.Line, e.Field, e.Message) + } + return fmt.Sprintf("validation error in %s: %s", e.Field, e.Message) +} + +// ValidationResult contains the results of validation +type ValidationResult struct { + Valid bool + Errors []ValidationError +} + +// Validator handles validation of changelog content and entries +type Validator struct { + // Regex patterns for validation + sectionHeaderRegex *regexp.Regexp + bulletPointRegex *regexp.Regexp + urlRegex *regexp.Regexp + issueRefRegex *regexp.Regexp +} + +// NewValidator creates a new changelog validator +func NewValidator() *Validator { + return &Validator{ + sectionHeaderRegex: regexp.MustCompile(`^##\s+(Added|Changed|Fixed|Deprecated|Removed|Security)\s*$`), + bulletPointRegex: regexp.MustCompile(`^[\s]*[-*]\s+(.+)$`), + urlRegex: regexp.MustCompile(`https?://[^\s]+`), + issueRefRegex: regexp.MustCompile(`#\d+`), + } +} + +// ValidateContent validates raw changelog content for proper formatting +func (v *Validator) ValidateContent(content string) ValidationResult { + result := ValidationResult{ + Valid: true, + Errors: []ValidationError{}, + } + + lines := strings.Split(content, "\n") + var currentSection string + var hasValidSections bool + var inExampleSection bool + lineNum := 0 + + for _, line := range lines { + lineNum++ + trimmedLine := strings.TrimSpace(line) + + // Skip empty lines and comments + if trimmedLine == "" || strings.HasPrefix(trimmedLine, "") { + continue + } + + // Skip the main title + if strings.HasPrefix(trimmedLine, "# Unreleased Changes") { + continue + } + + // Check if we're entering the example section + if strings.HasPrefix(trimmedLine, "---") || strings.HasPrefix(trimmedLine, "### Example Entries") { + inExampleSection = true + continue + } + + // Skip example section content + if inExampleSection { + continue + } + + // Check for section headers + if strings.HasPrefix(trimmedLine, "##") { + if matches := v.sectionHeaderRegex.FindStringSubmatch(trimmedLine); len(matches) > 1 { + currentSection = strings.ToLower(matches[1]) + hasValidSections = true + } else { + result.Valid = false + result.Errors = append(result.Errors, ValidationError{ + Field: "section_header", + Message: fmt.Sprintf("invalid section header format: '%s'. Expected format: '## SectionName'", trimmedLine), + Line: lineNum, + }) + } + continue + } + + // Check bullet points + if strings.HasPrefix(trimmedLine, "-") || strings.HasPrefix(trimmedLine, "*") { + if currentSection == "" { + result.Valid = false + result.Errors = append(result.Errors, ValidationError{ + Field: "bullet_point", + Message: "bullet point found outside of any section", + Line: lineNum, + }) + continue + } + + // Check for empty bullet points first (just "-" or "*" with optional whitespace) + if trimmedLine == "-" || trimmedLine == "*" || strings.TrimSpace(trimmedLine[1:]) == "" { + result.Valid = false + result.Errors = append(result.Errors, ValidationError{ + Field: "bullet_point", + Message: "empty bullet point content", + Line: lineNum, + }) + continue + } + + if matches := v.bulletPointRegex.FindStringSubmatch(trimmedLine); len(matches) > 1 { + content := strings.TrimSpace(matches[1]) + if content == "" { + result.Valid = false + result.Errors = append(result.Errors, ValidationError{ + Field: "bullet_point", + Message: "empty bullet point content", + Line: lineNum, + }) + } else { + // Validate bullet point content + v.validateBulletPointContent(content, lineNum, &result) + } + } else { + result.Valid = false + result.Errors = append(result.Errors, ValidationError{ + Field: "bullet_point", + Message: fmt.Sprintf("malformed bullet point: '%s'", trimmedLine), + Line: lineNum, + }) + } + continue + } + + // Check for unexpected content + if trimmedLine != "" && !strings.HasPrefix(trimmedLine, " + permonitorv2,permonitor + + + + + + + + + + \ No newline at end of file diff --git a/v3/internal/commands/update_cli.go b/v3/internal/commands/update_cli.go new file mode 100644 index 000000000..a7f51796f --- /dev/null +++ b/v3/internal/commands/update_cli.go @@ -0,0 +1,178 @@ +package commands + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/pterm/pterm" + "github.com/wailsapp/wails/v3/internal/debug" + "github.com/wailsapp/wails/v3/internal/github" + "github.com/wailsapp/wails/v3/internal/term" + "github.com/wailsapp/wails/v3/internal/version" +) + +type UpdateCLIOptions struct { + NoColour bool `name:"n" description:"Disable colour output"` + PreRelease bool `name:"pre" description:"Update to the latest pre-release (eg beta)"` + Version string `name:"version" description:"Update to a specific version (eg v3.0.0)"` + Latest bool `name:"latest" description:"Install the latest stable release"` +} + +func UpdateCLI(options *UpdateCLIOptions) error { + if options.NoColour { + term.DisableColor() + } + + term.Header("Update CLI") + + // Check if this CLI has been installed from vcs + if debug.LocalModulePath != "" && !options.Latest { + v3Path := filepath.ToSlash(debug.LocalModulePath + "/v3") + term.Println("This Wails CLI has been installed from source. To update to the latest stable release, run the following commands in the `" + v3Path + "` directory:") + term.Println(" - git pull") + term.Println(" - wails3 task install") + term.Println("") + term.Println("If you want to install the latest release, please run `wails3 update cli -latest`") + return nil + } + + if options.Latest { + latestVersion, err := github.GetLatestStableRelease() + if err != nil { + return err + } + return updateToVersion(latestVersion, true, version.String()) + } + + term.Println("Checking for updates...") + + var desiredVersion *github.SemanticVersion + var err error + var valid bool + + if len(options.Version) > 0 { + // Check if this is a valid version + valid, err = github.IsValidTag(options.Version) + if err == nil { + if !valid { + err = fmt.Errorf("version '%s' is invalid", options.Version) + } else { + desiredVersion, err = github.NewSemanticVersion(options.Version) + } + } + } else { + if options.PreRelease { + desiredVersion, err = github.GetLatestPreRelease() + } else { + desiredVersion, err = github.GetLatestStableRelease() + if err != nil { + pterm.Println("") + pterm.Println("No stable release found for this major version. To update to the latest pre-release (eg beta), run:") + pterm.Println(" wails3 update cli -pre") + return nil + } + } + } + if err != nil { + return err + } + pterm.Println() + + currentVersion := version.String() + pterm.Printf(" Current Version : %s\n", currentVersion) + + if len(options.Version) > 0 { + fmt.Printf(" Desired Version : v%s\n", desiredVersion) + } else { + if options.PreRelease { + fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion) + } else { + fmt.Printf(" Latest Release : v%s\n", desiredVersion) + } + } + + return updateToVersion(desiredVersion, len(options.Version) > 0, currentVersion) +} + +func updateToVersion(targetVersion *github.SemanticVersion, force bool, currentVersion string) error { + targetVersionString := "v" + targetVersion.String() + + if targetVersionString == currentVersion { + pterm.Println("\nLooks like you're up to date!") + return nil + } + + var desiredVersion string + + if !force { + compareVersion := currentVersion + + currentVersion, err := github.NewSemanticVersion(compareVersion) + if err != nil { + return err + } + + var success bool + + // Release -> Pre-Release = Massage current version to prerelease format + if targetVersion.IsPreRelease() && currentVersion.IsRelease() { + testVersion, err := github.NewSemanticVersion(compareVersion + "-0") + if err != nil { + return err + } + success, _ = targetVersion.IsGreaterThan(testVersion) + } + // Pre-Release -> Release = Massage target version to prerelease format + if targetVersion.IsRelease() && currentVersion.IsPreRelease() { + mainversion := currentVersion.MainVersion() + targetVersion, err = github.NewSemanticVersion(targetVersion.String()) + if err != nil { + return err + } + success, _ = targetVersion.IsGreaterThanOrEqual(mainversion) + } + + // Release -> Release = Standard check + if (targetVersion.IsRelease() && currentVersion.IsRelease()) || + (targetVersion.IsPreRelease() && currentVersion.IsPreRelease()) { + success, _ = targetVersion.IsGreaterThan(currentVersion) + } + + // Compare + if !success { + pterm.Println("Error: The requested version is lower than the current version.") + pterm.Printf("If this is what you really want to do, use `wails3 update cli -version %s`\n", targetVersionString) + return nil + } + + desiredVersion = "v" + targetVersion.String() + } else { + desiredVersion = "v" + targetVersion.String() + } + + pterm.Println() + pterm.Print("Installing Wails CLI " + desiredVersion + "...") + + // Run command in non module directory + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("cannot find home directory: %w", err) + } + + cmd := exec.Command("go", "install", "github.com/wailsapp/wails/v3/cmd/wails@"+desiredVersion) + cmd.Dir = homeDir + sout, serr := cmd.CombinedOutput() + if err := cmd.Run(); err != nil { + pterm.Println("Failed.") + pterm.Error.Println(string(sout) + "\n" + serr.Error()) + return err + } + pterm.Println("Done.") + pterm.Println("\nMake sure you update your project go.mod file to use " + desiredVersion + ":") + pterm.Println(" require github.com/wailsapp/wails/v3 " + desiredVersion) + pterm.Println("\nTo view the release notes, please run `wails3 releasenotes`") + + return nil +} diff --git a/v3/internal/commands/version.go b/v3/internal/commands/version.go new file mode 100644 index 000000000..65bf66113 --- /dev/null +++ b/v3/internal/commands/version.go @@ -0,0 +1,14 @@ +package commands + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/internal/version" +) + +type VersionOptions struct{} + +func Version(_ *VersionOptions) error { + DisableFooter = true + println(version.String()) + return nil +} diff --git a/v3/internal/commands/watcher.go b/v3/internal/commands/watcher.go new file mode 100644 index 000000000..13f83ad61 --- /dev/null +++ b/v3/internal/commands/watcher.go @@ -0,0 +1,65 @@ +package commands + +import ( + "github.com/atterpac/refresh/engine" + "github.com/wailsapp/wails/v3/internal/signal" + "gopkg.in/yaml.v3" + "os" +) + +type WatcherOptions struct { + Config string `description:"The config file including path" default:"."` +} + +func Watcher(options *WatcherOptions) error { + stopChan := make(chan struct{}) + + // Parse the config file + type devConfig struct { + Config engine.Config `yaml:"dev_mode"` + } + + var devconfig devConfig + + // Parse the config file + c, err := os.ReadFile(options.Config) + if err != nil { + return err + } + err = yaml.Unmarshal(c, &devconfig) + if err != nil { + return err + } + + watcherEngine, err := engine.NewEngineFromConfig(devconfig.Config) + if err != nil { + return err + } + + // Setup cleanup function that stops the engine + cleanup := func() { + watcherEngine.Stop() + } + defer cleanup() + + // Signal handler needs to notify when to stop + signalCleanup := func() { + cleanup() + stopChan <- struct{}{} + } + + signalHandler := signal.NewSignalHandler(signalCleanup) + signalHandler.ExitMessage = func(sig os.Signal) string { + return "" + } + signalHandler.Start() + + // Start the engine + err = watcherEngine.Start() + if err != nil { + return err + } + + <-stopChan + return nil +} diff --git a/v3/internal/commands/webview2/MicrosoftEdgeWebview2Setup.exe b/v3/internal/commands/webview2/MicrosoftEdgeWebview2Setup.exe new file mode 100644 index 000000000..89a56ec16 Binary files /dev/null and b/v3/internal/commands/webview2/MicrosoftEdgeWebview2Setup.exe differ diff --git a/v3/internal/dbus/DbusMenu.xml b/v3/internal/dbus/DbusMenu.xml new file mode 100644 index 000000000..db6959845 --- /dev/null +++ b/v3/internal/dbus/DbusMenu.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v3/internal/dbus/README.md b/v3/internal/dbus/README.md new file mode 100644 index 000000000..d2cfce105 --- /dev/null +++ b/v3/internal/dbus/README.md @@ -0,0 +1,5 @@ +//Note that you need to have github.com/knightpp/dbus-codegen-go installed from "custom" branch + +//go:generate dbus-codegen-go -prefix org.kde -package notifier -output internal/generated/notifier/status_notifier_item.go internal/StatusNotifierItem.xml +//go:generate dbus-codegen-go -prefix com.canonical -package menu -output internal/generated/menu/dbus_menu.go internal/DbusMenu.xml + diff --git a/v3/internal/dbus/StatusNotifierItem.xml b/v3/internal/dbus/StatusNotifierItem.xml new file mode 100644 index 000000000..1093d3d18 --- /dev/null +++ b/v3/internal/dbus/StatusNotifierItem.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v3/internal/dbus/dbus.go b/v3/internal/dbus/dbus.go new file mode 100644 index 000000000..4e4a3adbd --- /dev/null +++ b/v3/internal/dbus/dbus.go @@ -0,0 +1,4 @@ +package dbus + +//go:generate dbus-codegen-go -prefix org.kde -package notifier -output notifier/status_notifier_item.go StatusNotifierItem.xml +//go:generate dbus-codegen-go -prefix com.canonical -package menu -output menu/dbus_menu.go DbusMenu.xml diff --git a/v3/internal/dbus/generate.sh b/v3/internal/dbus/generate.sh new file mode 100755 index 000000000..03716e522 --- /dev/null +++ b/v3/internal/dbus/generate.sh @@ -0,0 +1,2 @@ +dbus-codegen-go -prefix com.canonical -package menu -output dbus_menu.go DbusMenu.xml +dbus-codegen-go -prefix org.kde -package notifier -output status_notifier_item.go StatusNotifierItem.xml diff --git a/v3/internal/dbus/menu/.keep b/v3/internal/dbus/menu/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/v3/internal/dbus/menu/dbus_menu.go b/v3/internal/dbus/menu/dbus_menu.go new file mode 100644 index 000000000..cbf4fd320 --- /dev/null +++ b/v3/internal/dbus/menu/dbus_menu.go @@ -0,0 +1,483 @@ +// Code generated by dbus-codegen-go DO NOT EDIT. +package menu + +import ( + "context" + "errors" + "fmt" + "github.com/godbus/dbus/v5" + "github.com/godbus/dbus/v5/introspect" +) + +var ( + // Introspection for com.canonical.dbusmenu + IntrospectDataDbusmenu = introspect.Interface{ + Name: "com.canonical.dbusmenu", + Methods: []introspect.Method{{Name: "GetLayout", Args: []introspect.Arg{ + {Name: "parentId", Type: "i", Direction: "in"}, + {Name: "recursionDepth", Type: "i", Direction: "in"}, + {Name: "propertyNames", Type: "as", Direction: "in"}, + {Name: "revision", Type: "u", Direction: "out"}, + {Name: "layout", Type: "(ia{sv}av)", Direction: "out"}, + }}, + {Name: "GetGroupProperties", Args: []introspect.Arg{ + {Name: "ids", Type: "ai", Direction: "in"}, + {Name: "propertyNames", Type: "as", Direction: "in"}, + {Name: "properties", Type: "a(ia{sv})", Direction: "out"}, + }}, + {Name: "GetProperty", Args: []introspect.Arg{ + {Name: "id", Type: "i", Direction: "in"}, + {Name: "name", Type: "s", Direction: "in"}, + {Name: "value", Type: "v", Direction: "out"}, + }}, + {Name: "Event", Args: []introspect.Arg{ + {Name: "id", Type: "i", Direction: "in"}, + {Name: "eventId", Type: "s", Direction: "in"}, + {Name: "data", Type: "v", Direction: "in"}, + {Name: "timestamp", Type: "u", Direction: "in"}, + }}, + {Name: "EventGroup", Args: []introspect.Arg{ + {Name: "events", Type: "a(isvu)", Direction: "in"}, + {Name: "idErrors", Type: "ai", Direction: "out"}, + }}, + {Name: "AboutToShow", Args: []introspect.Arg{ + {Name: "id", Type: "i", Direction: "in"}, + {Name: "needUpdate", Type: "b", Direction: "out"}, + }}, + {Name: "AboutToShowGroup", Args: []introspect.Arg{ + {Name: "ids", Type: "ai", Direction: "in"}, + {Name: "updatesNeeded", Type: "ai", Direction: "out"}, + {Name: "idErrors", Type: "ai", Direction: "out"}, + }}, + }, + Signals: []introspect.Signal{{Name: "ItemsPropertiesUpdated", Args: []introspect.Arg{ + {Name: "updatedProps", Type: "a(ia{sv})", Direction: "out"}, + {Name: "removedProps", Type: "a(ias)", Direction: "out"}, + }}, + {Name: "LayoutUpdated", Args: []introspect.Arg{ + {Name: "revision", Type: "u", Direction: "out"}, + {Name: "parent", Type: "i", Direction: "out"}, + }}, + {Name: "ItemActivationRequested", Args: []introspect.Arg{ + {Name: "id", Type: "i", Direction: "out"}, + {Name: "timestamp", Type: "u", Direction: "out"}, + }}, + }, + Properties: []introspect.Property{{Name: "Version", Type: "u", Access: "read"}, + {Name: "TextDirection", Type: "s", Access: "read"}, + {Name: "Status", Type: "s", Access: "read"}, + {Name: "IconThemePath", Type: "as", Access: "read"}, + }, + Annotations: []introspect.Annotation{}, + } +) + +// Signal is a common interface for all signals. +type Signal interface { + Name() string + Interface() string + Sender() string + + path() dbus.ObjectPath + values() []interface{} +} + +// Emit sends the given signal to the bus. +func Emit(conn *dbus.Conn, s Signal) error { + return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...) +} + +// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved. +var ErrUnknownSignal = errors.New("unknown signal") + +// LookupSignal converts the given raw D-Bus signal with variable body +// into one with typed structured body or returns ErrUnknownSignal error. +func LookupSignal(signal *dbus.Signal) (Signal, error) { + switch signal.Name { + case InterfaceDbusmenu + "." + "ItemsPropertiesUpdated": + v0, ok := signal.Body[0].([]struct { + V0 int32 + V1 map[string]dbus.Variant + }) + if !ok { + return nil, fmt.Errorf("prop .UpdatedProps is %T, not []struct {V0 int32;V1 map[string]dbus.Variant}", signal.Body[0]) + } + v1, ok := signal.Body[1].([]struct { + V0 int32 + V1 []string + }) + if !ok { + return nil, fmt.Errorf("prop .RemovedProps is %T, not []struct {V0 int32;V1 []string}", signal.Body[1]) + } + return &Dbusmenu_ItemsPropertiesUpdatedSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &Dbusmenu_ItemsPropertiesUpdatedSignalBody{ + UpdatedProps: v0, + RemovedProps: v1, + }, + }, nil + case InterfaceDbusmenu + "." + "LayoutUpdated": + v0, ok := signal.Body[0].(uint32) + if !ok { + return nil, fmt.Errorf("prop .Revision is %T, not uint32", signal.Body[0]) + } + v1, ok := signal.Body[1].(int32) + if !ok { + return nil, fmt.Errorf("prop .Parent is %T, not int32", signal.Body[1]) + } + return &Dbusmenu_LayoutUpdatedSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &Dbusmenu_LayoutUpdatedSignalBody{ + Revision: v0, + Parent: v1, + }, + }, nil + case InterfaceDbusmenu + "." + "ItemActivationRequested": + v0, ok := signal.Body[0].(int32) + if !ok { + return nil, fmt.Errorf("prop .Id is %T, not int32", signal.Body[0]) + } + v1, ok := signal.Body[1].(uint32) + if !ok { + return nil, fmt.Errorf("prop .Timestamp is %T, not uint32", signal.Body[1]) + } + return &Dbusmenu_ItemActivationRequestedSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &Dbusmenu_ItemActivationRequestedSignalBody{ + Id: v0, + Timestamp: v1, + }, + }, nil + default: + return nil, ErrUnknownSignal + } +} + +// AddMatchSignal registers a match rule for the given signal, +// opts are appended to the automatically generated signal's rules. +func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error { + return conn.AddMatchSignal(append([]dbus.MatchOption{ + dbus.WithMatchInterface(s.Interface()), + dbus.WithMatchMember(s.Name()), + }, opts...)...) +} + +// RemoveMatchSignal unregisters the previously registered subscription. +func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error { + return conn.RemoveMatchSignal(append([]dbus.MatchOption{ + dbus.WithMatchInterface(s.Interface()), + dbus.WithMatchMember(s.Name()), + }, opts...)...) +} + +// Interface name constants. +const ( + InterfaceDbusmenu = "com.canonical.dbusmenu" +) + +// Dbusmenuer is com.canonical.dbusmenu interface. +type Dbusmenuer interface { + // GetLayout is com.canonical.dbusmenu.GetLayout method. + GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct { + V0 int32 + V1 map[string]dbus.Variant + V2 []dbus.Variant + }, err *dbus.Error) + // GetGroupProperties is com.canonical.dbusmenu.GetGroupProperties method. + GetGroupProperties(ids []int32, propertyNames []string) (properties []struct { + V0 int32 + V1 map[string]dbus.Variant + }, err *dbus.Error) + // GetProperty is com.canonical.dbusmenu.GetProperty method. + GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) + // Event is com.canonical.dbusmenu.Event method. + Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error) + // EventGroup is com.canonical.dbusmenu.EventGroup method. + EventGroup(events []struct { + V0 int32 + V1 string + V2 dbus.Variant + V3 uint32 + }) (idErrors []int32, err *dbus.Error) + // AboutToShow is com.canonical.dbusmenu.AboutToShow method. + AboutToShow(id int32) (needUpdate bool, err *dbus.Error) + // AboutToShowGroup is com.canonical.dbusmenu.AboutToShowGroup method. + AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) +} + +// ExportDbusmenu exports the given object that implements com.canonical.dbusmenu on the bus. +func ExportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath, v Dbusmenuer) error { + return conn.ExportSubtreeMethodTable(map[string]interface{}{ + "GetLayout": v.GetLayout, + "GetGroupProperties": v.GetGroupProperties, + "GetProperty": v.GetProperty, + "Event": v.Event, + "EventGroup": v.EventGroup, + "AboutToShow": v.AboutToShow, + "AboutToShowGroup": v.AboutToShowGroup, + }, path, InterfaceDbusmenu) +} + +// UnexportDbusmenu unexports com.canonical.dbusmenu interface on the named path. +func UnexportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath) error { + return conn.Export(nil, path, InterfaceDbusmenu) +} + +// UnimplementedDbusmenu can be embedded to have forward compatible server implementations. +type UnimplementedDbusmenu struct{} + +func (*UnimplementedDbusmenu) iface() string { + return InterfaceDbusmenu +} + +func (*UnimplementedDbusmenu) GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct { + V0 int32 + V1 map[string]dbus.Variant + V2 []dbus.Variant +}, err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedDbusmenu) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct { + V0 int32 + V1 map[string]dbus.Variant +}, err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedDbusmenu) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedDbusmenu) Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedDbusmenu) EventGroup(events []struct { + V0 int32 + V1 string + V2 dbus.Variant + V3 uint32 +}) (idErrors []int32, err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedDbusmenu) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedDbusmenu) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +// NewDbusmenu creates and allocates com.canonical.dbusmenu. +func NewDbusmenu(object dbus.BusObject) *Dbusmenu { + return &Dbusmenu{object} +} + +// Dbusmenu implements com.canonical.dbusmenu D-Bus interface. +type Dbusmenu struct { + object dbus.BusObject +} + +// GetLayout calls com.canonical.dbusmenu.GetLayout method. +func (o *Dbusmenu) GetLayout(ctx context.Context, parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct { + V0 int32 + V1 map[string]dbus.Variant + V2 []dbus.Variant +}, err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetLayout", 0, parentId, recursionDepth, propertyNames).Store(&revision, &layout) + return +} + +// GetGroupProperties calls com.canonical.dbusmenu.GetGroupProperties method. +func (o *Dbusmenu) GetGroupProperties(ctx context.Context, ids []int32, propertyNames []string) (properties []struct { + V0 int32 + V1 map[string]dbus.Variant +}, err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetGroupProperties", 0, ids, propertyNames).Store(&properties) + return +} + +// GetProperty calls com.canonical.dbusmenu.GetProperty method. +func (o *Dbusmenu) GetProperty(ctx context.Context, id int32, name string) (value dbus.Variant, err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetProperty", 0, id, name).Store(&value) + return +} + +// Event calls com.canonical.dbusmenu.Event method. +func (o *Dbusmenu) Event(ctx context.Context, id int32, eventId string, data dbus.Variant, timestamp uint32) (err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".Event", 0, id, eventId, data, timestamp).Store() + return +} + +// EventGroup calls com.canonical.dbusmenu.EventGroup method. +func (o *Dbusmenu) EventGroup(ctx context.Context, events []struct { + V0 int32 + V1 string + V2 dbus.Variant + V3 uint32 +}) (idErrors []int32, err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".EventGroup", 0, events).Store(&idErrors) + return +} + +// AboutToShow calls com.canonical.dbusmenu.AboutToShow method. +func (o *Dbusmenu) AboutToShow(ctx context.Context, id int32) (needUpdate bool, err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShow", 0, id).Store(&needUpdate) + return +} + +// AboutToShowGroup calls com.canonical.dbusmenu.AboutToShowGroup method. +func (o *Dbusmenu) AboutToShowGroup(ctx context.Context, ids []int32) (updatesNeeded []int32, idErrors []int32, err error) { + err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShowGroup", 0, ids).Store(&updatesNeeded, &idErrors) + return +} + +// GetVersion gets com.canonical.dbusmenu.Version property. +func (o *Dbusmenu) GetVersion(ctx context.Context) (version uint32, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Version").Store(&version) + return +} + +// GetTextDirection gets com.canonical.dbusmenu.TextDirection property. +func (o *Dbusmenu) GetTextDirection(ctx context.Context) (textDirection string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "TextDirection").Store(&textDirection) + return +} + +// GetStatus gets com.canonical.dbusmenu.Status property. +func (o *Dbusmenu) GetStatus(ctx context.Context) (status string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Status").Store(&status) + return +} + +// GetIconThemePath gets com.canonical.dbusmenu.IconThemePath property. +func (o *Dbusmenu) GetIconThemePath(ctx context.Context) (iconThemePath []string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "IconThemePath").Store(&iconThemePath) + return +} + +// Dbusmenu_ItemsPropertiesUpdatedSignal represents com.canonical.dbusmenu.ItemsPropertiesUpdated signal. +type Dbusmenu_ItemsPropertiesUpdatedSignal struct { + sender string + Path dbus.ObjectPath + Body *Dbusmenu_ItemsPropertiesUpdatedSignalBody +} + +// Name returns the signal's name. +func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Name() string { + return "ItemsPropertiesUpdated" +} + +// Interface returns the signal's interface. +func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Interface() string { + return InterfaceDbusmenu +} + +// Sender returns the signal's sender unique name. +func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Sender() string { + return s.sender +} + +func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) values() []interface{} { + return []interface{}{s.Body.UpdatedProps, s.Body.RemovedProps} +} + +// Dbusmenu_ItemsPropertiesUpdatedSignalBody is body container. +type Dbusmenu_ItemsPropertiesUpdatedSignalBody struct { + UpdatedProps []struct { + V0 int32 + V1 map[string]dbus.Variant + } + RemovedProps []struct { + V0 int32 + V1 []string + } +} + +// Dbusmenu_LayoutUpdatedSignal represents com.canonical.dbusmenu.LayoutUpdated signal. +type Dbusmenu_LayoutUpdatedSignal struct { + sender string + Path dbus.ObjectPath + Body *Dbusmenu_LayoutUpdatedSignalBody +} + +// Name returns the signal's name. +func (s *Dbusmenu_LayoutUpdatedSignal) Name() string { + return "LayoutUpdated" +} + +// Interface returns the signal's interface. +func (s *Dbusmenu_LayoutUpdatedSignal) Interface() string { + return InterfaceDbusmenu +} + +// Sender returns the signal's sender unique name. +func (s *Dbusmenu_LayoutUpdatedSignal) Sender() string { + return s.sender +} + +func (s *Dbusmenu_LayoutUpdatedSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *Dbusmenu_LayoutUpdatedSignal) values() []interface{} { + return []interface{}{s.Body.Revision, s.Body.Parent} +} + +// Dbusmenu_LayoutUpdatedSignalBody is body container. +type Dbusmenu_LayoutUpdatedSignalBody struct { + Revision uint32 + Parent int32 +} + +// Dbusmenu_ItemActivationRequestedSignal represents com.canonical.dbusmenu.ItemActivationRequested signal. +type Dbusmenu_ItemActivationRequestedSignal struct { + sender string + Path dbus.ObjectPath + Body *Dbusmenu_ItemActivationRequestedSignalBody +} + +// Name returns the signal's name. +func (s *Dbusmenu_ItemActivationRequestedSignal) Name() string { + return "ItemActivationRequested" +} + +// Interface returns the signal's interface. +func (s *Dbusmenu_ItemActivationRequestedSignal) Interface() string { + return InterfaceDbusmenu +} + +// Sender returns the signal's sender unique name. +func (s *Dbusmenu_ItemActivationRequestedSignal) Sender() string { + return s.sender +} + +func (s *Dbusmenu_ItemActivationRequestedSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *Dbusmenu_ItemActivationRequestedSignal) values() []interface{} { + return []interface{}{s.Body.Id, s.Body.Timestamp} +} + +// Dbusmenu_ItemActivationRequestedSignalBody is body container. +type Dbusmenu_ItemActivationRequestedSignalBody struct { + Id int32 + Timestamp uint32 +} diff --git a/v3/internal/dbus/notifier/.keep b/v3/internal/dbus/notifier/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/v3/internal/dbus/notifier/status_notifier_item.go b/v3/internal/dbus/notifier/status_notifier_item.go new file mode 100644 index 000000000..998916440 --- /dev/null +++ b/v3/internal/dbus/notifier/status_notifier_item.go @@ -0,0 +1,636 @@ +// Code generated by dbus-codegen-go DO NOT EDIT. +package notifier + +import ( + "context" + "errors" + "fmt" + "github.com/godbus/dbus/v5" + "github.com/godbus/dbus/v5/introspect" +) + +var ( + // Introspection for org.kde.StatusNotifierItem + IntrospectDataStatusNotifierItem = introspect.Interface{ + Name: "org.kde.StatusNotifierItem", + Methods: []introspect.Method{{Name: "ContextMenu", Args: []introspect.Arg{ + {Name: "x", Type: "i", Direction: "in"}, + {Name: "y", Type: "i", Direction: "in"}, + }}, + {Name: "Activate", Args: []introspect.Arg{ + {Name: "x", Type: "i", Direction: "in"}, + {Name: "y", Type: "i", Direction: "in"}, + }}, + {Name: "SecondaryActivate", Args: []introspect.Arg{ + {Name: "x", Type: "i", Direction: "in"}, + {Name: "y", Type: "i", Direction: "in"}, + }}, + {Name: "Scroll", Args: []introspect.Arg{ + {Name: "delta", Type: "i", Direction: "in"}, + {Name: "orientation", Type: "s", Direction: "in"}, + }}, + }, + Signals: []introspect.Signal{{Name: "NewTitle"}, + {Name: "NewIcon"}, + {Name: "NewAttentionIcon"}, + {Name: "NewOverlayIcon"}, + {Name: "NewStatus", Args: []introspect.Arg{ + {Name: "status", Type: "s", Direction: ""}, + }}, + {Name: "NewIconThemePath", Args: []introspect.Arg{ + {Name: "icon_theme_path", Type: "s", Direction: "out"}, + }}, + {Name: "NewMenu"}, + }, + Properties: []introspect.Property{{Name: "Category", Type: "s", Access: "read"}, + {Name: "Id", Type: "s", Access: "read"}, + {Name: "Title", Type: "s", Access: "read"}, + {Name: "Status", Type: "s", Access: "read"}, + {Name: "WindowId", Type: "i", Access: "read"}, + {Name: "IconThemePath", Type: "s", Access: "read"}, + {Name: "Menu", Type: "o", Access: "read"}, + {Name: "ItemIsMenu", Type: "b", Access: "read"}, + {Name: "IconName", Type: "s", Access: "read"}, + {Name: "IconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{ + {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"}, + }}, + {Name: "OverlayIconName", Type: "s", Access: "read"}, + {Name: "OverlayIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{ + {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"}, + }}, + {Name: "AttentionIconName", Type: "s", Access: "read"}, + {Name: "AttentionIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{ + {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"}, + }}, + {Name: "AttentionMovieName", Type: "s", Access: "read"}, + {Name: "ToolTip", Type: "(sa(iiay)ss)", Access: "read", Annotations: []introspect.Annotation{ + {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusToolTipStruct"}, + }}, + }, + Annotations: []introspect.Annotation{}, + } +) + +// Signal is a common interface for all signals. +type Signal interface { + Name() string + Interface() string + Sender() string + + path() dbus.ObjectPath + values() []interface{} +} + +// Emit sends the given signal to the bus. +func Emit(conn *dbus.Conn, s Signal) error { + return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...) +} + +// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved. +var ErrUnknownSignal = errors.New("unknown signal") + +// LookupSignal converts the given raw D-Bus signal with variable body +// into one with typed structured body or returns ErrUnknownSignal error. +func LookupSignal(signal *dbus.Signal) (Signal, error) { + switch signal.Name { + case InterfaceStatusNotifierItem + "." + "NewTitle": + return &StatusNotifierItem_NewTitleSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewTitleSignalBody{}, + }, nil + case InterfaceStatusNotifierItem + "." + "NewIcon": + return &StatusNotifierItem_NewIconSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewIconSignalBody{}, + }, nil + case InterfaceStatusNotifierItem + "." + "NewAttentionIcon": + return &StatusNotifierItem_NewAttentionIconSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewAttentionIconSignalBody{}, + }, nil + case InterfaceStatusNotifierItem + "." + "NewOverlayIcon": + return &StatusNotifierItem_NewOverlayIconSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewOverlayIconSignalBody{}, + }, nil + case InterfaceStatusNotifierItem + "." + "NewStatus": + v0, ok := signal.Body[0].(string) + if !ok { + return nil, fmt.Errorf("prop .Status is %T, not string", signal.Body[0]) + } + return &StatusNotifierItem_NewStatusSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewStatusSignalBody{ + Status: v0, + }, + }, nil + case InterfaceStatusNotifierItem + "." + "NewIconThemePath": + v0, ok := signal.Body[0].(string) + if !ok { + return nil, fmt.Errorf("prop .IconThemePath is %T, not string", signal.Body[0]) + } + return &StatusNotifierItem_NewIconThemePathSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewIconThemePathSignalBody{ + IconThemePath: v0, + }, + }, nil + case InterfaceStatusNotifierItem + "." + "NewMenu": + return &StatusNotifierItem_NewMenuSignal{ + sender: signal.Sender, + Path: signal.Path, + Body: &StatusNotifierItem_NewMenuSignalBody{}, + }, nil + default: + return nil, ErrUnknownSignal + } +} + +// AddMatchSignal registers a match rule for the given signal, +// opts are appended to the automatically generated signal's rules. +func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error { + return conn.AddMatchSignal(append([]dbus.MatchOption{ + dbus.WithMatchInterface(s.Interface()), + dbus.WithMatchMember(s.Name()), + }, opts...)...) +} + +// RemoveMatchSignal unregisters the previously registered subscription. +func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error { + return conn.RemoveMatchSignal(append([]dbus.MatchOption{ + dbus.WithMatchInterface(s.Interface()), + dbus.WithMatchMember(s.Name()), + }, opts...)...) +} + +// Interface name constants. +const ( + InterfaceStatusNotifierItem = "org.kde.StatusNotifierItem" +) + +// StatusNotifierItemer is org.kde.StatusNotifierItem interface. +type StatusNotifierItemer interface { + // ContextMenu is org.kde.StatusNotifierItem.ContextMenu method. + ContextMenu(x int32, y int32) (err *dbus.Error) + // Activate is org.kde.StatusNotifierItem.Activate method. + Activate(x int32, y int32) (err *dbus.Error) + // SecondaryActivate is org.kde.StatusNotifierItem.SecondaryActivate method. + SecondaryActivate(x int32, y int32) (err *dbus.Error) + // Scroll is org.kde.StatusNotifierItem.Scroll method. + Scroll(delta int32, orientation string) (err *dbus.Error) +} + +// ExportStatusNotifierItem exports the given object that implements org.kde.StatusNotifierItem on the bus. +func ExportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath, v StatusNotifierItemer) error { + return conn.ExportSubtreeMethodTable(map[string]interface{}{ + "ContextMenu": v.ContextMenu, + "Activate": v.Activate, + "SecondaryActivate": v.SecondaryActivate, + "Scroll": v.Scroll, + }, path, InterfaceStatusNotifierItem) +} + +// UnexportStatusNotifierItem unexports org.kde.StatusNotifierItem interface on the named path. +func UnexportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath) error { + return conn.Export(nil, path, InterfaceStatusNotifierItem) +} + +// UnimplementedStatusNotifierItem can be embedded to have forward compatible server implementations. +type UnimplementedStatusNotifierItem struct{} + +func (*UnimplementedStatusNotifierItem) iface() string { + return InterfaceStatusNotifierItem +} + +func (*UnimplementedStatusNotifierItem) ContextMenu(x int32, y int32) (err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedStatusNotifierItem) Activate(x int32, y int32) (err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedStatusNotifierItem) SecondaryActivate(x int32, y int32) (err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +func (*UnimplementedStatusNotifierItem) Scroll(delta int32, orientation string) (err *dbus.Error) { + err = &dbus.ErrMsgUnknownMethod + return +} + +// NewStatusNotifierItem creates and allocates org.kde.StatusNotifierItem. +func NewStatusNotifierItem(object dbus.BusObject) *StatusNotifierItem { + return &StatusNotifierItem{object} +} + +// StatusNotifierItem implements org.kde.StatusNotifierItem D-Bus interface. +type StatusNotifierItem struct { + object dbus.BusObject +} + +// ContextMenu calls org.kde.StatusNotifierItem.ContextMenu method. +func (o *StatusNotifierItem) ContextMenu(ctx context.Context, x int32, y int32) (err error) { + err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".ContextMenu", 0, x, y).Store() + return +} + +// Activate calls org.kde.StatusNotifierItem.Activate method. +func (o *StatusNotifierItem) Activate(ctx context.Context, x int32, y int32) (err error) { + err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Activate", 0, x, y).Store() + return +} + +// SecondaryActivate calls org.kde.StatusNotifierItem.SecondaryActivate method. +func (o *StatusNotifierItem) SecondaryActivate(ctx context.Context, x int32, y int32) (err error) { + err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".SecondaryActivate", 0, x, y).Store() + return +} + +// Scroll calls org.kde.StatusNotifierItem.Scroll method. +func (o *StatusNotifierItem) Scroll(ctx context.Context, delta int32, orientation string) (err error) { + err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Scroll", 0, delta, orientation).Store() + return +} + +// GetCategory gets org.kde.StatusNotifierItem.Category property. +func (o *StatusNotifierItem) GetCategory(ctx context.Context) (category string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Category").Store(&category) + return +} + +// GetId gets org.kde.StatusNotifierItem.Id property. +func (o *StatusNotifierItem) GetId(ctx context.Context) (id string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Id").Store(&id) + return +} + +// GetTitle gets org.kde.StatusNotifierItem.Title property. +func (o *StatusNotifierItem) GetTitle(ctx context.Context) (title string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Title").Store(&title) + return +} + +// GetStatus gets org.kde.StatusNotifierItem.Status property. +func (o *StatusNotifierItem) GetStatus(ctx context.Context) (status string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Status").Store(&status) + return +} + +// GetWindowId gets org.kde.StatusNotifierItem.WindowId property. +func (o *StatusNotifierItem) GetWindowId(ctx context.Context) (windowId int32, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "WindowId").Store(&windowId) + return +} + +// GetIconThemePath gets org.kde.StatusNotifierItem.IconThemePath property. +func (o *StatusNotifierItem) GetIconThemePath(ctx context.Context) (iconThemePath string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconThemePath").Store(&iconThemePath) + return +} + +// GetMenu gets org.kde.StatusNotifierItem.Menu property. +func (o *StatusNotifierItem) GetMenu(ctx context.Context) (menu dbus.ObjectPath, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Menu").Store(&menu) + return +} + +// GetItemIsMenu gets org.kde.StatusNotifierItem.ItemIsMenu property. +func (o *StatusNotifierItem) GetItemIsMenu(ctx context.Context) (itemIsMenu bool, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ItemIsMenu").Store(&itemIsMenu) + return +} + +// GetIconName gets org.kde.StatusNotifierItem.IconName property. +func (o *StatusNotifierItem) GetIconName(ctx context.Context) (iconName string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconName").Store(&iconName) + return +} + +// GetIconPixmap gets org.kde.StatusNotifierItem.IconPixmap property. +// +// Annotations: +// +// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector +func (o *StatusNotifierItem) GetIconPixmap(ctx context.Context) (iconPixmap []struct { + V0 int32 + V1 int32 + V2 []byte +}, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconPixmap").Store(&iconPixmap) + return +} + +// GetOverlayIconName gets org.kde.StatusNotifierItem.OverlayIconName property. +func (o *StatusNotifierItem) GetOverlayIconName(ctx context.Context) (overlayIconName string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconName").Store(&overlayIconName) + return +} + +// GetOverlayIconPixmap gets org.kde.StatusNotifierItem.OverlayIconPixmap property. +// +// Annotations: +// +// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector +func (o *StatusNotifierItem) GetOverlayIconPixmap(ctx context.Context) (overlayIconPixmap []struct { + V0 int32 + V1 int32 + V2 []byte +}, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconPixmap").Store(&overlayIconPixmap) + return +} + +// GetAttentionIconName gets org.kde.StatusNotifierItem.AttentionIconName property. +func (o *StatusNotifierItem) GetAttentionIconName(ctx context.Context) (attentionIconName string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconName").Store(&attentionIconName) + return +} + +// GetAttentionIconPixmap gets org.kde.StatusNotifierItem.AttentionIconPixmap property. +// +// Annotations: +// +// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector +func (o *StatusNotifierItem) GetAttentionIconPixmap(ctx context.Context) (attentionIconPixmap []struct { + V0 int32 + V1 int32 + V2 []byte +}, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconPixmap").Store(&attentionIconPixmap) + return +} + +// GetAttentionMovieName gets org.kde.StatusNotifierItem.AttentionMovieName property. +func (o *StatusNotifierItem) GetAttentionMovieName(ctx context.Context) (attentionMovieName string, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionMovieName").Store(&attentionMovieName) + return +} + +// GetToolTip gets org.kde.StatusNotifierItem.ToolTip property. +// +// Annotations: +// +// @org.qtproject.QtDBus.QtTypeName = KDbusToolTipStruct +func (o *StatusNotifierItem) GetToolTip(ctx context.Context) (toolTip struct { + V0 string + V1 []struct { + V0 int32 + V1 int32 + V2 []byte + } + V2 string + V3 string +}, err error) { + err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ToolTip").Store(&toolTip) + return +} + +// StatusNotifierItem_NewTitleSignal represents org.kde.StatusNotifierItem.NewTitle signal. +type StatusNotifierItem_NewTitleSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewTitleSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewTitleSignal) Name() string { + return "NewTitle" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewTitleSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewTitleSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewTitleSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewTitleSignal) values() []interface{} { + return []interface{}{} +} + +// StatusNotifierItem_NewTitleSignalBody is body container. +type StatusNotifierItem_NewTitleSignalBody struct { +} + +// StatusNotifierItem_NewIconSignal represents org.kde.StatusNotifierItem.NewIcon signal. +type StatusNotifierItem_NewIconSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewIconSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewIconSignal) Name() string { + return "NewIcon" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewIconSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewIconSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewIconSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewIconSignal) values() []interface{} { + return []interface{}{} +} + +// StatusNotifierItem_NewIconSignalBody is body container. +type StatusNotifierItem_NewIconSignalBody struct { +} + +// StatusNotifierItem_NewAttentionIconSignal represents org.kde.StatusNotifierItem.NewAttentionIcon signal. +type StatusNotifierItem_NewAttentionIconSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewAttentionIconSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewAttentionIconSignal) Name() string { + return "NewAttentionIcon" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewAttentionIconSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewAttentionIconSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewAttentionIconSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewAttentionIconSignal) values() []interface{} { + return []interface{}{} +} + +// StatusNotifierItem_NewAttentionIconSignalBody is body container. +type StatusNotifierItem_NewAttentionIconSignalBody struct { +} + +// StatusNotifierItem_NewOverlayIconSignal represents org.kde.StatusNotifierItem.NewOverlayIcon signal. +type StatusNotifierItem_NewOverlayIconSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewOverlayIconSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewOverlayIconSignal) Name() string { + return "NewOverlayIcon" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewOverlayIconSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewOverlayIconSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewOverlayIconSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewOverlayIconSignal) values() []interface{} { + return []interface{}{} +} + +// StatusNotifierItem_NewOverlayIconSignalBody is body container. +type StatusNotifierItem_NewOverlayIconSignalBody struct { +} + +// StatusNotifierItem_NewStatusSignal represents org.kde.StatusNotifierItem.NewStatus signal. +type StatusNotifierItem_NewStatusSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewStatusSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewStatusSignal) Name() string { + return "NewStatus" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewStatusSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewStatusSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewStatusSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewStatusSignal) values() []interface{} { + return []interface{}{s.Body.Status} +} + +// StatusNotifierItem_NewStatusSignalBody is body container. +type StatusNotifierItem_NewStatusSignalBody struct { + Status string +} + +// StatusNotifierItem_NewIconThemePathSignal represents org.kde.StatusNotifierItem.NewIconThemePath signal. +type StatusNotifierItem_NewIconThemePathSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewIconThemePathSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewIconThemePathSignal) Name() string { + return "NewIconThemePath" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewIconThemePathSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewIconThemePathSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewIconThemePathSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewIconThemePathSignal) values() []interface{} { + return []interface{}{s.Body.IconThemePath} +} + +// StatusNotifierItem_NewIconThemePathSignalBody is body container. +type StatusNotifierItem_NewIconThemePathSignalBody struct { + IconThemePath string +} + +// StatusNotifierItem_NewMenuSignal represents org.kde.StatusNotifierItem.NewMenu signal. +type StatusNotifierItem_NewMenuSignal struct { + sender string + Path dbus.ObjectPath + Body *StatusNotifierItem_NewMenuSignalBody +} + +// Name returns the signal's name. +func (s *StatusNotifierItem_NewMenuSignal) Name() string { + return "NewMenu" +} + +// Interface returns the signal's interface. +func (s *StatusNotifierItem_NewMenuSignal) Interface() string { + return InterfaceStatusNotifierItem +} + +// Sender returns the signal's sender unique name. +func (s *StatusNotifierItem_NewMenuSignal) Sender() string { + return s.sender +} + +func (s *StatusNotifierItem_NewMenuSignal) path() dbus.ObjectPath { + return s.Path +} + +func (s *StatusNotifierItem_NewMenuSignal) values() []interface{} { + return []interface{}{} +} + +// StatusNotifierItem_NewMenuSignalBody is body container. +type StatusNotifierItem_NewMenuSignalBody struct { +} diff --git a/v3/internal/debug/debug.go b/v3/internal/debug/debug.go new file mode 100644 index 000000000..394688ce7 --- /dev/null +++ b/v3/internal/debug/debug.go @@ -0,0 +1,42 @@ +package debug + +import ( + "os" + "path/filepath" + "runtime" +) + +var LocalModulePath = "" + +func init() { + // Check if .git exists in the relative directory from here: ../../.. + // If it does, we are in a local build + gitDir := RelativePath("..", "..", "..", ".git") + if _, err := os.Stat(gitDir); err == nil { + modulePath := RelativePath("..", "..", "..") + LocalModulePath, _ = filepath.Abs(modulePath) + } +} + +// RelativePath returns a qualified path created by joining the +// directory of the calling file and the given relative path. +func RelativePath(relativepath string, optionalpaths ...string) string { + _, thisFile, _, _ := runtime.Caller(1) + localDir := filepath.Dir(thisFile) + + // If we have optional paths, join them to the relativepath + if len(optionalpaths) > 0 { + paths := []string{relativepath} + paths = append(paths, optionalpaths...) + relativepath = filepath.Join(paths...) + } + result, err := filepath.Abs(filepath.Join(localDir, relativepath)) + if err != nil { + // I'm allowing this for 1 reason only: It's fatal if the path + // supplied is wrong as it's only used internally in Wails. If we get + // that path wrong, we should know about it immediately. The other reason is + // that it cuts down a ton of unnecassary error handling. + panic(err) + } + return result +} diff --git a/v3/internal/doctor/diagnostics.go b/v3/internal/doctor/diagnostics.go new file mode 100644 index 000000000..bc6b36670 --- /dev/null +++ b/v3/internal/doctor/diagnostics.go @@ -0,0 +1,93 @@ +package doctor + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +// DiagnosticTest represents a single diagnostic test to be run +type DiagnosticTest struct { + Name string + Run func() (bool, string) // Returns success and error message if failed + HelpURL string +} + +// DiagnosticResult represents the result of a diagnostic test +type DiagnosticResult struct { + TestName string + ErrorMsg string + HelpURL string +} + +// platformDiagnostics maps platform names to their diagnostic tests +var platformDiagnostics = map[string][]DiagnosticTest{ + // Tests that run on all platforms + "all": { + { + Name: "Check Go installation", + Run: func() (bool, string) { + // This is just an example test for all platforms + if runtime.Version() == "" { + return false, "Go installation not found" + } + return true, "" + }, + HelpURL: "/getting-started/installation/", + }, + }, + // Platform specific tests + "darwin": { + { + Name: "Check for .syso file", + Run: func() (bool, string) { + // Check for .syso files in current directory + matches, err := filepath.Glob("*.syso") + if err != nil { + return false, "Error checking for .syso files" + } + if len(matches) > 0 { + return false, fmt.Sprintf("Found .syso file(s): %v. These may cause issues when building on macOS", strings.Join(matches, ", ")) + } + return true, "" + }, + HelpURL: "/troubleshooting/mac-syso", + }, + }, +} + +// RunDiagnostics executes all diagnostic tests for the current platform +func RunDiagnostics() []DiagnosticResult { + var results []DiagnosticResult + + // Run tests that apply to all platforms + if tests, exists := platformDiagnostics["all"]; exists { + for _, test := range tests { + success, errMsg := test.Run() + if !success { + results = append(results, DiagnosticResult{ + TestName: test.Name, + ErrorMsg: errMsg, + HelpURL: test.HelpURL, + }) + } + } + } + + // Run platform-specific tests + if tests, exists := platformDiagnostics[runtime.GOOS]; exists { + for _, test := range tests { + success, errMsg := test.Run() + if !success { + results = append(results, DiagnosticResult{ + TestName: test.Name, + ErrorMsg: errMsg, + HelpURL: test.HelpURL, + }) + } + } + } + + return results +} diff --git a/v3/internal/doctor/doctor.go b/v3/internal/doctor/doctor.go new file mode 100644 index 000000000..a14fcf3b3 --- /dev/null +++ b/v3/internal/doctor/doctor.go @@ -0,0 +1,274 @@ +package doctor + +import ( + "bytes" + "fmt" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "runtime/debug" + "slices" + "strconv" + "strings" + + "github.com/wailsapp/wails/v3/internal/term" + + "github.com/wailsapp/wails/v3/internal/buildinfo" + + "github.com/go-git/go-git/v5" + "github.com/jaypipes/ghw" + "github.com/pterm/pterm" + "github.com/samber/lo" + "github.com/wailsapp/wails/v3/internal/operatingsystem" + "github.com/wailsapp/wails/v3/internal/version" +) + +func Run() (err error) { + + get, err := buildinfo.Get() + if err != nil { + return err + } + _ = get + + term.Header("Wails Doctor") + + spinner, _ := pterm.DefaultSpinner.WithRemoveWhenDone().Start("Scanning system - Please wait (this may take a long time)...") + + defer func() { + if err != nil { + spinner.Fail() + } + }() + + /** Build **/ + + // BuildSettings contains the build settings for the application + var BuildSettings map[string]string + + // BuildInfo contains the build info for the application + var BuildInfo *debug.BuildInfo + + var ok bool + BuildInfo, ok = debug.ReadBuildInfo() + if !ok { + return fmt.Errorf("could not read build info from binary") + } + BuildSettings = lo.Associate(BuildInfo.Settings, func(setting debug.BuildSetting) (string, string) { + return setting.Key, setting.Value + }) + + /** Operating System **/ + + // Get system info + info, err := operatingsystem.Info() + if err != nil { + term.Error("Failed to get system information") + return err + } + + /** Wails **/ + + wailsPackage, _ := lo.Find(BuildInfo.Deps, func(dep *debug.Module) bool { + return dep.Path == "github.com/wailsapp/wails/v3" + }) + + wailsVersion := strings.TrimSpace(version.String()) + if wailsPackage != nil && wailsPackage.Replace != nil { + wailsVersion = "(local) => " + filepath.ToSlash(wailsPackage.Replace.Path) + // Get the latest commit hash + repo, err := git.PlainOpen(filepath.Join(wailsPackage.Replace.Path, "..")) + if err == nil { + head, err := repo.Head() + if err == nil { + wailsVersion += " (" + head.Hash().String()[:8] + ")" + } + } + } + + platformExtras, ok := getInfo() + + dependencies := make(map[string]string) + checkPlatformDependencies(dependencies, &ok) + + spinner.Success() + + /** Output **/ + + term.Section("System") + + systemTabledata := pterm.TableData{ + {pterm.Sprint("Name"), info.Name}, + {pterm.Sprint("Version"), info.Version}, + {pterm.Sprint("ID"), info.ID}, + {pterm.Sprint("Branding"), info.Branding}, + + {pterm.Sprint("Platform"), runtime.GOOS}, + {pterm.Sprint("Architecture"), runtime.GOARCH}, + } + + mapKeys := lo.Keys(platformExtras) + slices.Sort(mapKeys) + for _, key := range mapKeys { + systemTabledata = append(systemTabledata, []string{key, platformExtras[key]}) + } + + // Probe CPU + cpus, _ := ghw.CPU() + if cpus != nil { + prefix := "CPU" + for idx, cpu := range cpus.Processors { + if len(cpus.Processors) > 1 { + prefix = "CPU " + strconv.Itoa(idx+1) + } + systemTabledata = append(systemTabledata, []string{prefix, cpu.Model}) + } + } else { + systemTabledata = append(systemTabledata, []string{"CPU", "Unknown"}) + } + + // Probe GPU + gpu, _ := ghw.GPU(ghw.WithDisableWarnings()) + if gpu != nil { + for idx, card := range gpu.GraphicsCards { + details := "Unknown" + prefix := "GPU " + strconv.Itoa(idx+1) + if card.DeviceInfo != nil { + details = fmt.Sprintf("%s (%s) - Driver: %s ", card.DeviceInfo.Product.Name, card.DeviceInfo.Vendor.Name, card.DeviceInfo.Driver) + } + systemTabledata = append(systemTabledata, []string{prefix, details}) + } + } else { + if runtime.GOOS == "darwin" { + var numCoresValue string + cmd := exec.Command("sh", "-c", "ioreg -l | grep gpu-core-count") + output, err := cmd.Output() + if err == nil { + // Look for an `=` sign, optional spaces and then an integer + re := regexp.MustCompile(`= *(\d+)`) + matches := re.FindAllStringSubmatch(string(output), -1) + numCoresValue = "Unknown" + if len(matches) > 0 { + numCoresValue = matches[0][1] + } + + } + + // Run `system_profiler SPDisplaysDataType | grep Metal` + var metalSupport string + cmd = exec.Command("sh", "-c", "system_profiler SPDisplaysDataType | grep Metal") + output, err = cmd.Output() + if err == nil { + metalSupport = ", " + strings.TrimSpace(string(output)) + } + systemTabledata = append(systemTabledata, []string{"GPU", numCoresValue + " cores" + metalSupport}) + + } else { + systemTabledata = append(systemTabledata, []string{"GPU", "Unknown"}) + } + } + + memory, _ := ghw.Memory() + var memoryText = "Unknown" + if memory != nil { + memoryText = strconv.Itoa(int(memory.TotalPhysicalBytes/1024/1024/1024)) + "GB" + } else { + if runtime.GOOS == "darwin" { + cmd := exec.Command("sh", "-c", "system_profiler SPHardwareDataType | grep 'Memory'") + output, err := cmd.Output() + if err == nil { + output = bytes.Replace(output, []byte("Memory: "), []byte(""), 1) + memoryText = strings.TrimSpace(string(output)) + } + } + } + systemTabledata = append(systemTabledata, []string{"Memory", memoryText}) + + err = pterm.DefaultTable.WithBoxed().WithData(systemTabledata).Render() + if err != nil { + return err + } + + // Build Environment + + term.Section("Build Environment") + + tableData := pterm.TableData{ + {"Wails CLI", wailsVersion}, + {"Go Version", runtime.Version()}, + } + + if buildInfo, _ := debug.ReadBuildInfo(); buildInfo != nil { + buildSettingToName := map[string]string{ + "vcs.revision": "Revision", + "vcs.modified": "Modified", + } + for _, buildSetting := range buildInfo.Settings { + name := buildSettingToName[buildSetting.Key] + if name == "" { + continue + } + tableData = append(tableData, []string{name, buildSetting.Value}) + } + } + + mapKeys = lo.Keys(BuildSettings) + slices.Sort(mapKeys) + for _, key := range mapKeys { + tableData = append(tableData, []string{key, BuildSettings[key]}) + } + + err = pterm.DefaultTable.WithBoxed(true).WithData(tableData).Render() + if err != nil { + return err + } + + // Dependencies + term.Section("Dependencies") + dependenciesBox := pterm.DefaultBox.WithTitleBottomCenter().WithTitle(pterm.Gray("*") + " - Optional Dependency") + dependencyTableData := pterm.TableData{} + if len(dependencies) == 0 { + pterm.Info.Println("No dependencies found") + } else { + var optionals pterm.TableData + mapKeys = lo.Keys(dependencies) + for _, key := range mapKeys { + if strings.HasPrefix(dependencies[key], "*") { + optionals = append(optionals, []string{key, dependencies[key]}) + } else { + dependencyTableData = append(dependencyTableData, []string{key, dependencies[key]}) + } + } + dependencyTableData = append(dependencyTableData, optionals...) + dependenciesTableString, _ := pterm.DefaultTable.WithData(dependencyTableData).Srender() + dependenciesBox.Println(dependenciesTableString) + } + + // Run diagnostics after system info + term.Section("Checking for issues") + + diagnosticResults := RunDiagnostics() + if len(diagnosticResults) == 0 { + pterm.Success.Println("No issues found") + } else { + pterm.Warning.Println("Found potential issues:") + for _, result := range diagnosticResults { + pterm.Printf("• %s: %s\n", result.TestName, result.ErrorMsg) + url := result.HelpURL + if strings.HasPrefix(url, "/") { + url = "https://v3.wails.io" + url + } + pterm.Printf(" For more information: %s\n", term.Hyperlink(url, url)) + } + } + + term.Section("Diagnosis") + if !ok { + term.Warning("There are some items above that need addressing!") + } else { + term.Success("Your system is ready for Wails development!") + } + + return nil +} diff --git a/v3/internal/doctor/doctor_common.go b/v3/internal/doctor/doctor_common.go new file mode 100644 index 000000000..4befcffe8 --- /dev/null +++ b/v3/internal/doctor/doctor_common.go @@ -0,0 +1,32 @@ +package doctor + +import ( + "bytes" + "os/exec" + "strconv" + "strings" +) + +func checkCommonDependencies(result map[string]string, ok *bool) { + // Check for npm + npmVersion := []byte("Not Installed. Requires npm >= 7.0.0") + npmVersion, err := exec.Command("npm", "-v").Output() + if err != nil { + *ok = false + } else { + npmVersion = bytes.TrimSpace(npmVersion) + // Check that it's at least version 7 by converting first byte to int and checking if it's >= 7 + // Parse the semver string + semver := strings.Split(string(npmVersion), ".") + if len(semver) > 0 { + major, _ := strconv.Atoi(semver[0]) + if major < 7 { + *ok = false + npmVersion = append(npmVersion, []byte(". Installed, but requires npm >= 7.0.0")...) + } else { + *ok = true + } + } + } + result["npm"] = string(npmVersion) +} diff --git a/v3/internal/doctor/doctor_darwin.go b/v3/internal/doctor/doctor_darwin.go new file mode 100644 index 000000000..9ffb368f1 --- /dev/null +++ b/v3/internal/doctor/doctor_darwin.go @@ -0,0 +1,63 @@ +//go:build darwin + +package doctor + +import ( + "bytes" + "github.com/samber/lo" + "os/exec" + "strings" + "syscall" +) + +func getSysctl(name string) string { + value, err := syscall.Sysctl(name) + if err != nil { + return "unknown" + } + return value +} + +func getInfo() (map[string]string, bool) { + result := make(map[string]string) + ok := true + + // Determine if the app is running on Apple Silicon + // Credit: https://www.yellowduck.be/posts/detecting-apple-silicon-via-go/ + appleSilicon := "unknown" + r, err := syscall.Sysctl("sysctl.proc_translated") + if err == nil { + appleSilicon = lo.Ternary(r == "\x00\x00\x00" || r == "\x01\x00\x00", "true", "false") + } + result["Apple Silicon"] = appleSilicon + result["CPU"] = getSysctl("machdep.cpu.brand_string") + + return result, ok +} + +func checkPlatformDependencies(result map[string]string, ok *bool) { + + // Check for xcode command line tools + output, err := exec.Command("xcode-select", "-v").Output() + cliToolsVersion := "N/A. Install by running: `xcode-select --install`" + if err != nil { + *ok = false + } else { + cliToolsVersion = strings.TrimPrefix(string(output), "xcode-select version ") + cliToolsVersion = strings.TrimSpace(cliToolsVersion) + cliToolsVersion = strings.TrimSuffix(cliToolsVersion, ".") + } + result["Xcode cli tools"] = cliToolsVersion + + checkCommonDependencies(result, ok) + + // Check for nsis + nsisVersion := []byte("Not Installed. Install with `brew install makensis`.") + output, err = exec.Command("makensis", "-VERSION").Output() + if err == nil && output != nil { + nsisVersion = output + } + nsisVersion = bytes.TrimSpace(nsisVersion) + + result["*NSIS"] = string(nsisVersion) +} diff --git a/v3/internal/doctor/doctor_linux.go b/v3/internal/doctor/doctor_linux.go new file mode 100644 index 000000000..a3b0e4ac5 --- /dev/null +++ b/v3/internal/doctor/doctor_linux.go @@ -0,0 +1,42 @@ +//go:build linux + +package doctor + +import ( + "github.com/wailsapp/wails/v3/internal/doctor/packagemanager" + "github.com/wailsapp/wails/v3/internal/operatingsystem" +) + +func getInfo() (map[string]string, bool) { + result := make(map[string]string) + return result, true +} + +func checkPlatformDependencies(result map[string]string, ok *bool) { + info, _ := operatingsystem.Info() + + pm := packagemanager.Find(info.ID) + deps, _ := packagemanager.Dependencies(pm) + for _, dep := range deps { + var status string + + switch true { + case !dep.Installed: + if dep.Optional { + status = "[Optional] " + } else { + *ok = false + } + status += "not installed." + if dep.InstallCommand != "" { + status += " Install with: " + dep.InstallCommand + } + case dep.Version != "": + status = dep.Version + } + + result[dep.Name] = status + } + + checkCommonDependencies(result, ok) +} diff --git a/v3/internal/doctor/doctor_test.go b/v3/internal/doctor/doctor_test.go new file mode 100644 index 000000000..98f4c3f8a --- /dev/null +++ b/v3/internal/doctor/doctor_test.go @@ -0,0 +1,10 @@ +package doctor + +import "testing" + +func TestRun(t *testing.T) { + err := Run() + if err != nil { + t.Errorf("TestRun failed: %v", err) + } +} diff --git a/v3/internal/doctor/doctor_windows.go b/v3/internal/doctor/doctor_windows.go new file mode 100644 index 000000000..2ae1dd2c8 --- /dev/null +++ b/v3/internal/doctor/doctor_windows.go @@ -0,0 +1,86 @@ +//go:build windows + +package doctor + +import ( + "os/exec" + "strings" + + "github.com/samber/lo" + "github.com/wailsapp/go-webview2/webviewloader" +) + +func getInfo() (map[string]string, bool) { + ok := true + result := make(map[string]string) + result["Go WebView2Loader"] = lo.Ternary(webviewloader.UsingGoWebview2Loader, "true", "false") + webviewVersion, err := webviewloader.GetAvailableCoreWebView2BrowserVersionString("") + if err != nil { + ok = false + webviewVersion = "Error:" + err.Error() + } + result["WebView2 Version"] = webviewVersion + return result, ok +} + +func getNSISVersion() string { + // Execute nsis + output, err := exec.Command("makensis", "-VERSION").Output() + if err != nil { + return "Not Installed" + } + return string(output) +} + +func getMakeAppxVersion() string { + // Check if MakeAppx.exe is available (part of Windows SDK) + _, err := exec.LookPath("MakeAppx.exe") + if err != nil { + return "Not Installed" + } + return "Installed" +} + +func getMSIXPackagingToolVersion() string { + // Check if MSIX Packaging Tool is installed + // Use PowerShell to check if the app is installed from Microsoft Store + cmd := exec.Command("powershell", "-Command", "Get-AppxPackage -Name Microsoft.MSIXPackagingTool") + output, err := cmd.Output() + if err != nil || len(output) == 0 || !strings.Contains(string(output), "Microsoft.MSIXPackagingTool") { + return "Not Installed" + } + + if strings.Contains(string(output), "Version") { + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if strings.Contains(line, "Version") { + parts := strings.Split(line, ":") + if len(parts) > 1 { + return strings.TrimSpace(parts[1]) + } + } + } + } + + return "Installed (Version Unknown)" +} + +func getSignToolVersion() string { + // Check if signtool.exe is available (part of Windows SDK) + _, err := exec.LookPath("signtool.exe") + if err != nil { + return "Not Installed" + } + return "Installed" +} + +func checkPlatformDependencies(result map[string]string, ok *bool) { + checkCommonDependencies(result, ok) + // add nsis + result["NSIS"] = getNSISVersion() + + // Add MSIX tooling checks + result["MakeAppx.exe (Windows SDK)"] = getMakeAppxVersion() + result["MSIX Packaging Tool"] = getMSIXPackagingToolVersion() + result["SignTool.exe (Windows SDK)"] = getSignToolVersion() +} diff --git a/v3/internal/doctor/packagemanager/apt.go b/v3/internal/doctor/packagemanager/apt.go new file mode 100644 index 000000000..257279081 --- /dev/null +++ b/v3/internal/doctor/packagemanager/apt.go @@ -0,0 +1,96 @@ +//go:build linux + +package packagemanager + +import ( + "regexp" + "strings" +) + +// Apt represents the Apt manager +type Apt struct { + name string + osid string +} + +// NewApt creates a new Apt instance +func NewApt(osid string) *Apt { + return &Apt{ + name: "apt", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (a *Apt) Packages() Packagemap { + return Packagemap{ + "gtk3": []*Package{ + {Name: "libgtk-3-dev", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: "libwebkit2gtk-4.1-dev", SystemPackage: true, Library: true}, + {Name: "libwebkit2gtk-4.0-dev", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "build-essential", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (a *Apt) Name() string { + return a.name +} + +func (a *Apt) listPackage(name string) (string, error) { + return execCmd("apt", "list", "-qq", name) +} + +// PackageInstalled tests if the given package name is installed +func (a *Apt) PackageInstalled(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + if pkg.InstallCheck != nil { + return pkg.InstallCheck(), nil + } + return false, nil + } + output, err := a.listPackage(pkg.Name) + // apt list -qq returns "all" if you have packages installed globally and locally + return strings.Contains(output, "installed") || strings.Contains(output, " all"), err +} + +// PackageAvailable tests if the given package is available for installation +func (a *Apt) PackageAvailable(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + return true, nil + } + output, err := a.listPackage(pkg.Name) + // We add a space to ensure we get a full match, not partial match + escapechars, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`) + escapechars.ReplaceAllString(output, "") + installed := strings.HasPrefix(output, pkg.Name) + a.getPackageVersion(pkg, output) + return installed, err +} + +// InstallCommand returns the package manager specific command to install a package +func (a *Apt) InstallCommand(pkg *Package) string { + if !pkg.SystemPackage { + return pkg.InstallCommand + } + return "sudo apt install " + pkg.Name +} + +func (a *Apt) getPackageVersion(pkg *Package, output string) { + splitOutput := strings.Split(output, " ") + if len(splitOutput) > 1 { + pkg.Version = splitOutput[1] + } +} diff --git a/v3/internal/doctor/packagemanager/dnf.go b/v3/internal/doctor/packagemanager/dnf.go new file mode 100644 index 000000000..029a38d67 --- /dev/null +++ b/v3/internal/doctor/packagemanager/dnf.go @@ -0,0 +1,119 @@ +//go:build linux + +package packagemanager + +import ( + "os/exec" + "strings" +) + +// Dnf represents the Dnf manager +type Dnf struct { + name string + osid string +} + +// NewDnf creates a new Dnf instance +func NewDnf(osid string) *Dnf { + return &Dnf{ + name: "dnf", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (y *Dnf) Packages() Packagemap { + return Packagemap{ + "gtk3": []*Package{ + {Name: "gtk3-devel", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: "webkit2gtk4.1-devel", SystemPackage: true, Library: true}, + {Name: "webkit2gtk3-devel", SystemPackage: true, Library: true}, + {Name: "webkit2gtk4.0-devel", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc-c++", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkgconf-pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm", SystemPackage: true}, + {Name: "nodejs-npm", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (y *Dnf) Name() string { + return y.name +} + +// PackageInstalled tests if the given package name is installed +func (y *Dnf) PackageInstalled(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + if pkg.InstallCheck != nil { + return pkg.InstallCheck(), nil + } + return false, nil + } + stdout, err := execCmd("dnf", "info", "installed", pkg.Name) + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + splitoutput := strings.Split(stdout, "\n") + for _, line := range splitoutput { + if strings.HasPrefix(line, "Version") { + splitline := strings.Split(line, ":") + pkg.Version = strings.TrimSpace(splitline[1]) + } + } + + return true, err +} + +// PackageAvailable tests if the given package is available for installation +func (y *Dnf) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, err := execCmd("dnf", "info", pkg.Name) + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + splitoutput := strings.Split(stdout, "\n") + for _, line := range splitoutput { + if strings.HasPrefix(line, "Version") { + splitline := strings.Split(line, ":") + pkg.Version = strings.TrimSpace(splitline[1]) + } + } + return true, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (y *Dnf) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand + } + return "sudo dnf install " + pkg.Name +} + +func (y *Dnf) getPackageVersion(pkg *Package, output string) { + splitOutput := strings.Split(output, " ") + if len(splitOutput) > 0 { + pkg.Version = splitOutput[1] + } +} diff --git a/v3/internal/doctor/packagemanager/emerge.go b/v3/internal/doctor/packagemanager/emerge.go new file mode 100644 index 000000000..301955051 --- /dev/null +++ b/v3/internal/doctor/packagemanager/emerge.go @@ -0,0 +1,113 @@ +//go:build linux + +package packagemanager + +import ( + "os/exec" + "regexp" + "strings" +) + +// Emerge represents the Emerge package manager +type Emerge struct { + name string + osid string +} + +// NewEmerge creates a new Emerge instance +func NewEmerge(osid string) *Emerge { + return &Emerge{ + name: "emerge", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (e *Emerge) Packages() Packagemap { + return Packagemap{ + "gtk3": []*Package{ + {Name: "x11-libs/gtk+", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: "net-libs/webkit-gtk:6", SystemPackage: true, Library: true}, + {Name: "net-libs/webkit-gtk:4", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "sys-devel/gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "dev-util/pkgconf", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "net-libs/nodejs", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (e *Emerge) Name() string { + return e.name +} + +// PackageInstalled tests if the given package name is installed +func (e *Emerge) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, err := execCmd("emerge", "-s", pkg.Name+"$") + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + regex := `.*\*\s+` + regexp.QuoteMeta(pkg.Name) + `\n(?:\S|\s)+?Latest version installed: (.*)` + installedRegex := regexp.MustCompile(regex) + matches := installedRegex.FindStringSubmatch(stdout) + pkg.Version = "" + noOfMatches := len(matches) + installed := false + if noOfMatches > 1 && matches[1] != "[ Not Installed ]" { + installed = true + pkg.Version = strings.TrimSpace(matches[1]) + } + return installed, err +} + +// PackageAvailable tests if the given package is available for installation +func (e *Emerge) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, err := execCmd("emerge", "-s", pkg.Name+"$") + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + installedRegex := regexp.MustCompile(`.*\*\s+` + regexp.QuoteMeta(pkg.Name) + `\n(?:\S|\s)+?Latest version available: (.*)`) + matches := installedRegex.FindStringSubmatch(stdout) + pkg.Version = "" + noOfMatches := len(matches) + available := false + if noOfMatches > 1 { + available = true + pkg.Version = strings.TrimSpace(matches[1]) + } + return available, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (e *Emerge) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand + } + return "sudo emerge " + pkg.Name +} diff --git a/v3/internal/doctor/packagemanager/eopkg.go b/v3/internal/doctor/packagemanager/eopkg.go new file mode 100644 index 000000000..060e59595 --- /dev/null +++ b/v3/internal/doctor/packagemanager/eopkg.go @@ -0,0 +1,111 @@ +//go:build linux + +package packagemanager + +import ( + "regexp" + "strings" +) + +type Eopkg struct { + name string + osid string +} + +// NewEopkg creates a new Eopkg instance +func NewEopkg(osid string) *Eopkg { + result := &Eopkg{ + name: "eopkg", + osid: osid, + } + result.intialiseName() + return result +} + +// Packages returns the packages that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (e *Eopkg) Packages() Packagemap { + return Packagemap{ + "gtk3": []*Package{ + {Name: "libgtk-3-devel", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: "libwebkit-gtk-devel", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "nodejs", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (e *Eopkg) Name() string { + return e.name +} + +// PackageInstalled tests if the given package is installed +func (e *Eopkg) PackageInstalled(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + if pkg.InstallCheck != nil { + return pkg.InstallCheck(), nil + } + return false, nil + } + stdout, err := execCmd("eopkg", "info", pkg.Name) + return strings.HasPrefix(stdout, "Installed"), err +} + +// PackageAvailable tests if the given package is available for installation +func (e *Eopkg) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, err := execCmd("eopkg", "info", pkg.Name) + // We add a space to ensure we get a full match, not partial match + output := e.removeEscapeSequences(stdout) + installed := strings.Contains(output, "Package found in Solus repository") + e.getPackageVersion(pkg, output) + return installed, err +} + +// InstallCommand returns the package manager specific command to install a package +func (e *Eopkg) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand + } + return "sudo eopkg it " + pkg.Name +} + +func (e *Eopkg) removeEscapeSequences(in string) string { + escapechars, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`) + return escapechars.ReplaceAllString(in, "") +} + +func (e *Eopkg) intialiseName() { + result := "eopkg" + stdout, err := execCmd("eopkg", "--version") + if err == nil { + result = strings.TrimSpace(stdout) + } + e.name = result +} + +func (e *Eopkg) getPackageVersion(pkg *Package, output string) { + + versionRegex := regexp.MustCompile(`.*Name.*version:\s+(.*)+, release: (.*)`) + matches := versionRegex.FindStringSubmatch(output) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + pkg.Version = matches[1] + if noOfMatches > 2 { + pkg.Version += " (r" + matches[2] + ")" + } + } +} diff --git a/v3/internal/doctor/packagemanager/nixpkgs.go b/v3/internal/doctor/packagemanager/nixpkgs.go new file mode 100644 index 000000000..4141de056 --- /dev/null +++ b/v3/internal/doctor/packagemanager/nixpkgs.go @@ -0,0 +1,151 @@ +//go:build linux + +package packagemanager + +import ( + "encoding/json" +) + +// Nixpkgs represents the Nixpkgs manager +type Nixpkgs struct { + name string + osid string +} + +type NixPackageDetail struct { + Name string + Pname string + Version string +} + +var available map[string]NixPackageDetail + +// NewNixpkgs creates a new Nixpkgs instance +func NewNixpkgs(osid string) *Nixpkgs { + available = map[string]NixPackageDetail{} + + return &Nixpkgs{ + name: "nixpkgs", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (n *Nixpkgs) Packages() Packagemap { + // Currently, only support checking the default channel. + channel := "nixpkgs" + if n.osid == "nixos" { + channel = "nixos" + } + + return Packagemap{ + "gtk3": []*Package{ + {Name: channel + ".gtk3", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: channel + ".webkitgtk", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: channel + ".gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: channel + ".pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: channel + ".nodejs", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (n *Nixpkgs) Name() string { + return n.name +} + +// PackageInstalled tests if the given package name is installed +func (n *Nixpkgs) PackageInstalled(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + if pkg.InstallCheck != nil { + return pkg.InstallCheck(), nil + } + return false, nil + } + + stdout, err := execCmd("nix-env", "--json", "-qA", pkg.Name) + if err != nil { + return false, nil + } + + var attributes map[string]NixPackageDetail + err = json.Unmarshal([]byte(stdout), &attributes) + if err != nil { + return false, err + } + + // Did we get one? + installed := false + for attribute, detail := range attributes { + if attribute == pkg.Name { + installed = true + pkg.Version = detail.Version + } + break + } + + // If on NixOS, package may be installed via system config, so check the nix store. + detail, ok := available[pkg.Name] + if !installed && n.osid == "nixos" && ok { + cmd := "nix-store --query --requisites /run/current-system | cut -d- -f2- | sort | uniq | grep '^" + detail.Pname + "'" + + if pkg.Library { + cmd += " | grep 'dev$'" + } + + stdout, err = execCmd("sh", "-c", cmd) + if err != nil { + return false, nil + } + + if len(stdout) > 0 { + installed = true + } + } + + return installed, nil +} + +// PackageAvailable tests if the given package is available for installation +func (n *Nixpkgs) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + + stdout, err := execCmd("nix-env", "--json", "-qaA", pkg.Name) + if err != nil { + return false, nil + } + + var attributes map[string]NixPackageDetail + err = json.Unmarshal([]byte(stdout), &attributes) + if err != nil { + return false, err + } + + // Grab first version. + for attribute, detail := range attributes { + pkg.Version = detail.Version + available[attribute] = detail + break + } + + return len(pkg.Version) > 0, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (n *Nixpkgs) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand + } + return "nix-env -iA " + pkg.Name +} diff --git a/v3/internal/doctor/packagemanager/packagemanager.go b/v3/internal/doctor/packagemanager/packagemanager.go new file mode 100644 index 000000000..800b05f7a --- /dev/null +++ b/v3/internal/doctor/packagemanager/packagemanager.go @@ -0,0 +1,169 @@ +//go:build linux + +package packagemanager + +import ( + "bytes" + "os" + "os/exec" + "sort" + "strings" +) + +func execCmd(command string, args ...string) (string, error) { + cmd := exec.Command(command, args...) + var stdo, stde bytes.Buffer + cmd.Stdout = &stdo + cmd.Stderr = &stde + cmd.Env = append(os.Environ(), "LANGUAGE=en_US.utf-8") + err := cmd.Run() + return stdo.String(), err +} + +// A list of package manager commands +var pmcommands = []string{ + "eopkg", + "apt", + "dnf", + "pacman", + "emerge", + "zypper", + "nix-env", +} + +// commandExists returns true if the given command can be found on the shell +func commandExists(name string) bool { + _, err := exec.LookPath(name) + if err != nil { + return false + } + return true +} + +// Find will attempt to find the system package manager +func Find(osid string) PackageManager { + + // Loop over pmcommands + for _, pmname := range pmcommands { + if commandExists(pmname) { + return newPackageManager(pmname, osid) + } + } + return nil +} + +func newPackageManager(pmname string, osid string) PackageManager { + switch pmname { + case "eopkg": + return NewEopkg(osid) + case "apt": + return NewApt(osid) + case "dnf": + return NewDnf(osid) + case "pacman": + return NewPacman(osid) + case "emerge": + return NewEmerge(osid) + case "zypper": + return NewZypper(osid) + case "nix-env": + return NewNixpkgs(osid) + } + return nil +} + +// Dependencies scans the system for required dependencies +// Returns a list of dependencies search for, whether they were found +// and whether they were installed +func Dependencies(p PackageManager) (DependencyList, error) { + + var dependencies DependencyList + + for name, packages := range p.Packages() { + dependency := &Dependency{Name: name} + for _, pkg := range packages { + dependency.Optional = pkg.Optional + dependency.External = !pkg.SystemPackage + dependency.InstallCommand = p.InstallCommand(pkg) + packageavailable, err := p.PackageAvailable(pkg) + if err != nil { + return nil, err + } + if packageavailable { + dependency.Version = pkg.Version + dependency.PackageName = pkg.Name + installed, err := p.PackageInstalled(pkg) + if err != nil { + return nil, err + } + if installed { + dependency.Installed = true + dependency.Version = pkg.Version + if !pkg.SystemPackage { + dependency.Version = AppVersion(name) + } + } else { + dependency.InstallCommand = p.InstallCommand(pkg) + } + break + } + } + dependencies = append(dependencies, dependency) + } + + // Sort dependencies + sort.Slice(dependencies, func(i, j int) bool { + return dependencies[i].Name < dependencies[j].Name + }) + + return dependencies, nil +} + +// AppVersion returns the version for application related to the given package +func AppVersion(name string) string { + + if name == "gcc" { + return gccVersion() + } + + if name == "pkg-config" { + return pkgConfigVersion() + } + + if name == "npm" { + return npmVersion() + } + + return "" + +} + +func gccVersion() string { + + var version string + var err error + + // Try "-dumpfullversion" + version, err = execCmd("gcc", "-dumpfullversion") + if err != nil { + + // Try -dumpversion + // We ignore the error as this function is not for testing whether the + // application exists, only that we can get the version number + dumpversion, err := execCmd("gcc", "-dumpversion") + if err == nil { + version = dumpversion + } + } + return strings.TrimSpace(version) +} + +func pkgConfigVersion() string { + version, _ := execCmd("pkg-config", "--version") + return strings.TrimSpace(version) +} + +func npmVersion() string { + version, _ := execCmd("npm", "--version") + return strings.TrimSpace(version) +} diff --git a/v3/internal/doctor/packagemanager/pacman.go b/v3/internal/doctor/packagemanager/pacman.go new file mode 100644 index 000000000..48f24164c --- /dev/null +++ b/v3/internal/doctor/packagemanager/pacman.go @@ -0,0 +1,113 @@ +//go:build linux + +package packagemanager + +import ( + "os/exec" + "regexp" + "strings" +) + +// Pacman represents the Pacman package manager +type Pacman struct { + name string + osid string +} + +// NewPacman creates a new Pacman instance +func NewPacman(osid string) *Pacman { + return &Pacman{ + name: "pacman", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (p *Pacman) Packages() Packagemap { + return Packagemap{ + "gtk3": []*Package{ + {Name: "gtk3", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: "webkit2gtk-4.1", SystemPackage: true, Library: true}, + {Name: "webkit2gtk", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkgconf", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (p *Pacman) Name() string { + return p.name +} + +// PackageInstalled tests if the given package name is installed +func (p *Pacman) PackageInstalled(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + if pkg.InstallCheck != nil { + return pkg.InstallCheck(), nil + } + return false, nil + } + stdout, err := execCmd("pacman", "-Q", pkg.Name) + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + splitoutput := strings.Split(stdout, "\n") + for _, line := range splitoutput { + if strings.HasPrefix(line, pkg.Name) { + splitline := strings.Split(line, " ") + pkg.Version = strings.TrimSpace(splitline[1]) + } + } + + return true, err +} + +// PackageAvailable tests if the given package is available for installation +func (p *Pacman) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + output, err := execCmd("pacman", "-Si", pkg.Name) + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + reg := regexp.MustCompile(`.*Version.*?:\s+(.*)`) + matches := reg.FindStringSubmatch(output) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + pkg.Version = strings.TrimSpace(matches[1]) + } + + return true, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (p *Pacman) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand + } + return "sudo pacman -S " + pkg.Name +} diff --git a/v3/internal/doctor/packagemanager/pm.go b/v3/internal/doctor/packagemanager/pm.go new file mode 100644 index 000000000..6b6ced9c0 --- /dev/null +++ b/v3/internal/doctor/packagemanager/pm.go @@ -0,0 +1,65 @@ +//go:build linux + +package packagemanager + +// Package contains information about a system package +type Package struct { + Name string + Version string + InstallCommand string + InstallCheck func() bool + SystemPackage bool + Library bool + Optional bool +} + +type Packagemap = map[string][]*Package + +// PackageManager is a common interface across all package managers +type PackageManager interface { + Name() string + Packages() Packagemap + PackageInstalled(*Package) (bool, error) + PackageAvailable(*Package) (bool, error) + InstallCommand(*Package) string +} + +// Dependency represents a system package that we require +type Dependency struct { + Name string + PackageName string + Installed bool + InstallCommand string + Version string + Optional bool + External bool +} + +// DependencyList is a list of Dependency instances +type DependencyList []*Dependency + +// InstallAllRequiredCommand returns the command you need to use to install all required dependencies +func (d DependencyList) InstallAllRequiredCommand() string { + + result := "" + for _, dependency := range d { + if !dependency.Installed && !dependency.Optional { + result += " - " + dependency.Name + ": " + dependency.InstallCommand + "\n" + } + } + + return result +} + +// InstallAllOptionalCommand returns the command you need to use to install all optional dependencies +func (d DependencyList) InstallAllOptionalCommand() string { + + result := "" + for _, dependency := range d { + if !dependency.Installed && dependency.Optional { + result += " - " + dependency.Name + ": " + dependency.InstallCommand + "\n" + } + } + + return result +} diff --git a/v3/internal/doctor/packagemanager/zypper.go b/v3/internal/doctor/packagemanager/zypper.go new file mode 100644 index 000000000..7f24b0aca --- /dev/null +++ b/v3/internal/doctor/packagemanager/zypper.go @@ -0,0 +1,122 @@ +//go:build linux +// +build linux + +package packagemanager + +import ( + "os/exec" + "regexp" + "strings" +) + +// Zypper represents the Zypper package manager +type Zypper struct { + name string + osid string +} + +// NewZypper creates a new Zypper instance +func NewZypper(osid string) *Zypper { + return &Zypper{ + name: "zypper", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (z *Zypper) Packages() Packagemap { + return Packagemap{ + "gtk3": []*Package{ + {Name: "gtk3-devel", SystemPackage: true, Library: true}, + }, + "webkit2gtk": []*Package{ + {Name: "webkit2gtk4_1-devel", SystemPackage: true, Library: true}, + {Name: "webkit2gtk3-soup2-devel", SystemPackage: true, Library: true}, + {Name: "webkit2gtk3-devel", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc-c++", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkg-config", SystemPackage: true}, + {Name: "pkgconf-pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm10", SystemPackage: true}, + }, + } +} + +// Name returns the name of the package manager +func (z *Zypper) Name() string { + return z.name +} + +// PackageInstalled tests if the given package name is installed +func (z *Zypper) PackageInstalled(pkg *Package) (bool, error) { + if !pkg.SystemPackage { + if pkg.InstallCheck != nil { + return pkg.InstallCheck(), nil + } + return false, nil + } + stdout, err := execCmd("zypper", "info", pkg.Name) + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + reg := regexp.MustCompile(`.*Installed\s*:\s*(Yes)\s*`) + matches := reg.FindStringSubmatch(stdout) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + z.getPackageVersion(pkg, stdout) + } + return noOfMatches > 1, err +} + +// PackageAvailable tests if the given package is available for installation +func (z *Zypper) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, err := execCmd("zypper", "info", pkg.Name) + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + available := strings.Contains(stdout, "Information for package") + if available { + z.getPackageVersion(pkg, stdout) + } + + return available, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (z *Zypper) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand + } + return "sudo zypper in " + pkg.Name +} + +func (z *Zypper) getPackageVersion(pkg *Package, output string) { + + reg := regexp.MustCompile(`.*Version.*:(.*)`) + matches := reg.FindStringSubmatch(output) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + pkg.Version = strings.TrimSpace(matches[1]) + } +} diff --git a/v3/internal/fileexplorer/fileexplorer.go b/v3/internal/fileexplorer/fileexplorer.go new file mode 100644 index 000000000..bc0048f94 --- /dev/null +++ b/v3/internal/fileexplorer/fileexplorer.go @@ -0,0 +1,61 @@ +package fileexplorer + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "time" +) + +func OpenFileManager(path string, selectFile bool) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + path = os.ExpandEnv(path) + path = filepath.Clean(path) + absPath, err := filepath.Abs(path) + if err != nil { + return fmt.Errorf("failed to resolve the absolute path: %w", err) + } + path = absPath + if pathInfo, err := os.Stat(path); err != nil { + return fmt.Errorf("failed to access the specified path: %w", err) + } else { + selectFile = selectFile && !pathInfo.IsDir() + } + + var ( + ignoreExitCode bool = false + ) + + switch runtime.GOOS { + case "windows": + // NOTE: Disabling the exit code check on Windows system. Workaround for explorer.exe + // exit code handling (https://github.com/microsoft/WSL/issues/6565) + ignoreExitCode = true + case "darwin", "linux": + default: + return errors.New("unsupported platform: " + runtime.GOOS) + } + + explorerBin, explorerArgs, err := explorerBinArgs(path, selectFile) + if err != nil { + return fmt.Errorf("failed to determine the file explorer binary: %w", err) + } + + cmd := exec.CommandContext(ctx, explorerBin, explorerArgs...) + cmd.SysProcAttr = sysProcAttr(path, selectFile) + cmd.Stdout = nil + cmd.Stderr = nil + + if err := cmd.Run(); err != nil { + if !ignoreExitCode { + return fmt.Errorf("failed to open the file explorer: %w", err) + } + } + return nil +} diff --git a/v3/internal/fileexplorer/fileexplorer_darwin.go b/v3/internal/fileexplorer/fileexplorer_darwin.go new file mode 100644 index 000000000..ce16d1206 --- /dev/null +++ b/v3/internal/fileexplorer/fileexplorer_darwin.go @@ -0,0 +1,19 @@ +//go:build darwin + +package fileexplorer + +import "syscall" + +func explorerBinArgs(path string, selectFile bool) (string, []string, error) { + args := []string{} + if selectFile { + args = append(args, "-R") + } + + args = append(args, path) + return "open", args, nil +} + +func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr { + return &syscall.SysProcAttr{} +} diff --git a/v3/internal/fileexplorer/fileexplorer_linux.go b/v3/internal/fileexplorer/fileexplorer_linux.go new file mode 100644 index 000000000..bf0186470 --- /dev/null +++ b/v3/internal/fileexplorer/fileexplorer_linux.go @@ -0,0 +1,97 @@ +//go:build linux + +package fileexplorer + +import ( + "bytes" + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + + ini "gopkg.in/ini.v1" +) + +func explorerBinArgs(path string, selectFile bool) (string, []string, error) { + // Map of field codes to their replacements + var fieldCodes = map[string]string{ + "%d": "", + "%D": "", + "%n": "", + "%N": "", + "%v": "", + "%m": "", + "%f": path, + "%F": path, + "%u": pathToURI(path), + "%U": pathToURI(path), + } + fileManagerQuery := exec.Command("xdg-mime", "query", "default", "inode/directory") + buf := new(bytes.Buffer) + fileManagerQuery.Stdout = buf + fileManagerQuery.Stderr = nil + + if err := fileManagerQuery.Run(); err != nil { + return fallbackExplorerBinArgs(path, selectFile) + } + + desktopFile, err := findDesktopFile(strings.TrimSpace((buf.String()))) + if err != nil { + return fallbackExplorerBinArgs(path, selectFile) + } + + cfg, err := ini.Load(desktopFile) + if err != nil { + // Opting to fallback rather than fail + return fallbackExplorerBinArgs(path, selectFile) + } + + exec := cfg.Section("Desktop Entry").Key("Exec").String() + for fieldCode, replacement := range fieldCodes { + exec = strings.ReplaceAll(exec, fieldCode, replacement) + } + args := strings.Fields(exec) + if !strings.Contains(strings.Join(args, " "), path) { + args = append(args, path) + } + + return args[0], args[1:], nil +} + +func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr { + return &syscall.SysProcAttr{} +} + +func fallbackExplorerBinArgs(path string, selectFile bool) (string, []string, error) { + // NOTE: The linux fallback explorer opening is not supporting file selection + path = filepath.Dir(path) + return "xdg-open", []string{path}, nil +} + +func pathToURI(path string) string { + absPath, err := filepath.Abs(path) + if err != nil { + return path + } + return "file://" + url.PathEscape(absPath) +} + +func findDesktopFile(xdgFileName string) (string, error) { + paths := []string{ + filepath.Join(os.Getenv("XDG_DATA_HOME"), "applications"), + filepath.Join(os.Getenv("HOME"), ".local", "share", "applications"), + "/usr/share/applications", + } + + for _, path := range paths { + desktopFile := filepath.Join(path, xdgFileName) + if _, err := os.Stat(desktopFile); err == nil { + return desktopFile, nil + } + } + err := fmt.Errorf("desktop file not found: %s", xdgFileName) + return "", err +} diff --git a/v3/internal/fileexplorer/fileexplorer_test.go b/v3/internal/fileexplorer/fileexplorer_test.go new file mode 100644 index 000000000..8f7a9b39f --- /dev/null +++ b/v3/internal/fileexplorer/fileexplorer_test.go @@ -0,0 +1,83 @@ +package fileexplorer_test + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/wailsapp/wails/v3/internal/fileexplorer" +) + +// Credit: https://stackoverflow.com/a/50631395 +func skipCI(t *testing.T) { + if os.Getenv("CI") != "" { + t.Skip("Skipping testing in CI environment") + } +} + +func TestFileExplorer(t *testing.T) { + skipCI(t) + // TestFileExplorer verifies that the OpenFileManager function correctly handles: + // - Opening files in the native file manager across different platforms + // - Selecting files when the selectFile parameter is true + // - Various error conditions like non-existent paths + tempDir := t.TempDir() // Create a temporary directory for tests + + tests := []struct { + name string + path string + selectFile bool + expectedErr error + }{ + {"Open Existing File", tempDir, false, nil}, + {"Select Existing File", tempDir, true, nil}, + {"Non-Existent Path", "/path/does/not/exist", false, fmt.Errorf("failed to access the specified path: /path/does/not/exist")}, + {"Path with Special Characters", filepath.Join(tempDir, "test space.txt"), true, nil}, + {"No Permission Path", "/root/test.txt", false, fmt.Errorf("failed to open the file explorer: /root/test.txt")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Run("Windows", func(t *testing.T) { + runPlatformTest(t, "windows") + }) + t.Run("Linux", func(t *testing.T) { + runPlatformTest(t, "linux") + }) + t.Run("Darwin", func(t *testing.T) { + runPlatformTest(t, "darwin") + }) + }) + } +} + +func runPlatformTest(t *testing.T, platform string) { + if runtime.GOOS != platform { + t.Skipf("Skipping test on non-%s platform", strings.ToTitle(platform)) + } + + testFile := filepath.Join(t.TempDir(), "test.txt") + if err := os.WriteFile(testFile, []byte("Test file contents"), 0644); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + selectFile bool + }{ + {"OpenFile", false}, + {"SelectFile", true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := fileexplorer.OpenFileManager(testFile, test.selectFile) + if err != nil { + t.Errorf("OpenFileManager(%s, %v) error = %v", testFile, test.selectFile, err) + } + }) + } +} diff --git a/v3/internal/fileexplorer/fileexplorer_windows.go b/v3/internal/fileexplorer/fileexplorer_windows.go new file mode 100644 index 000000000..6498c5f26 --- /dev/null +++ b/v3/internal/fileexplorer/fileexplorer_windows.go @@ -0,0 +1,24 @@ +//go:build windows + +package fileexplorer + +import ( + "fmt" + "syscall" +) + +func explorerBinArgs(path string, selectFile bool) (string, []string, error) { + return "explorer.exe", []string{}, nil +} + +func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr { + if selectFile { + return &syscall.SysProcAttr{ + CmdLine: fmt.Sprintf("explorer.exe /select,\"%s\"", path), + } + } else { + return &syscall.SysProcAttr{ + CmdLine: fmt.Sprintf("explorer.exe \"%s\"", path), + } + } +} diff --git a/v3/internal/flags/bindings.go b/v3/internal/flags/bindings.go new file mode 100644 index 000000000..acef15006 --- /dev/null +++ b/v3/internal/flags/bindings.go @@ -0,0 +1,96 @@ +package flags + +import ( + "errors" + "slices" + "strings" + "unicode/utf8" +) + +type GenerateBindingsOptions struct { + BuildFlagsString string `name:"f" description:"A list of additional space-separated Go build flags. Flags (or parts of them) can be wrapped in single or double quotes to include spaces"` + OutputDirectory string `name:"d" description:"The output directory" default:"frontend/bindings"` + ModelsFilename string `name:"models" description:"File name for exported JS/TS models (excluding the extension)" default:"models"` + IndexFilename string `name:"index" description:"File name for JS/TS package indexes (excluding the extension)" default:"index"` + TS bool `name:"ts" description:"Generate Typescript bindings"` + UseInterfaces bool `name:"i" description:"Generate Typescript interfaces instead of classes"` + UseBundledRuntime bool `name:"b" description:"Use the bundled runtime instead of importing the npm package"` + UseNames bool `name:"names" description:"Use names instead of IDs for the binding calls"` + NoIndex bool `name:"noindex" description:"Do not generate JS/TS index files"` + DryRun bool `name:"dry" description:"Do not write output files"` + Silent bool `name:"silent" description:"Silent mode"` + Verbose bool `name:"v" description:"Enable debug output"` + Clean bool `name:"clean" description:"Clean output directory before generation" default:"true"` +} + +var ErrUnmatchedQuote = errors.New("build flags contain an unmatched quote") + +func isWhitespace(r rune) bool { + // We use Go's definition of whitespace instead of the Unicode ones + return r == ' ' || r == '\t' || r == '\r' || r == '\n' +} + +func isNonWhitespace(r rune) bool { + return !isWhitespace(r) +} + +func isQuote(r rune) bool { + return r == '\'' || r == '"' +} + +func isQuoteOrWhitespace(r rune) bool { + return isQuote(r) || isWhitespace(r) +} + +func (options *GenerateBindingsOptions) BuildFlags() (flags []string, err error) { + str := options.BuildFlagsString + + // temporary buffer for flag assembly + flag := make([]byte, 0, 32) + + for start := strings.IndexFunc(str, isNonWhitespace); start >= 0; start = strings.IndexFunc(str, isNonWhitespace) { + // each iteration starts at the beginning of a flag + // skip initial whitespace + str = str[start:] + + // iterate over all quoted and unquoted parts of the flag and join them + for { + breakpoint := strings.IndexFunc(str, isQuoteOrWhitespace) + if breakpoint < 0 { + breakpoint = len(str) + } + + // append everything up to the breakpoint + flag = append(flag, str[:breakpoint]...) + str = str[breakpoint:] + + quote, quoteSize := utf8.DecodeRuneInString(str) + if !isQuote(quote) { + // if the breakpoint is not a quote, we reached the end of the flag + break + } + + // otherwise, look for the closing quote + str = str[quoteSize:] + closingQuote := strings.IndexRune(str, quote) + + // closing quote not found, append everything to the last flag and raise an error + if closingQuote < 0 { + flag = append(flag, str...) + str = "" + err = ErrUnmatchedQuote + break + } + + // closing quote found, append quoted content to the flag and restart after the quote + flag = append(flag, str[:closingQuote]...) + str = str[closingQuote+quoteSize:] + } + + // append a clone of the flag to the result, then reuse buffer space + flags = append(flags, string(slices.Clone(flag))) + flag = flag[:0] + } + + return +} diff --git a/v3/internal/flags/bindings_test.go b/v3/internal/flags/bindings_test.go new file mode 100644 index 000000000..506ca1b00 --- /dev/null +++ b/v3/internal/flags/bindings_test.go @@ -0,0 +1,125 @@ +package flags + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +func TestBuildFlags(t *testing.T) { + tests := []struct { + name string + input string + wantFlags []string + wantErr bool + }{ + { + name: "empty string", + input: "", + wantFlags: nil, + }, + { + name: "single flag, multiple spaces", + input: " -v ", + wantFlags: []string{"-v"}, + }, + { + name: "multiple flags, complex spaces", + input: " \t-v\r\n-x", + wantFlags: []string{"-v", "-x"}, + }, + { + name: "empty flag (single quotes)", + input: `''`, + wantFlags: []string{""}, + }, + { + name: "empty flag (double quotes)", + input: `""`, + wantFlags: []string{""}, + }, + { + name: "flag with spaces (single quotes)", + input: `'a b'`, + wantFlags: []string{"a \tb"}, + }, + { + name: "flag with spaces (double quotes)", + input: `'a b'`, + wantFlags: []string{"a \tb"}, + }, + { + name: "mixed quoted and non-quoted flags (single quotes)", + input: `-v 'a b ' -x`, + wantFlags: []string{"-v", "a b ", "-x"}, + }, + { + name: "mixed quoted and non-quoted flags (double quotes)", + input: `-v "a b " -x`, + wantFlags: []string{"-v", "a b ", "-x"}, + }, + { + name: "mixed quoted and non-quoted flags (mixed quotes)", + input: `-v "a b " '-x'`, + wantFlags: []string{"-v", "a b ", "-x"}, + }, + { + name: "double quote within single quotes", + input: `' " '`, + wantFlags: []string{" \" "}, + }, + { + name: "single quote within double quotes", + input: `" ' "`, + wantFlags: []string{" ' "}, + }, + { + name: "unmatched single quote", + input: `-v "a b " '-x -y`, + wantFlags: []string{"-v", "a b ", "-x -y"}, + wantErr: true, + }, + { + name: "unmatched double quote", + input: `-v "a b " "-x -y`, + wantFlags: []string{"-v", "a b ", "-x -y"}, + wantErr: true, + }, + { + name: "mismatched single quote", + input: `-v "a b " '-x" -y`, + wantFlags: []string{"-v", "a b ", "-x\" -y"}, + wantErr: true, + }, + { + name: "mismatched double quote", + input: `-v "a b " "-x' -y`, + wantFlags: []string{"-v", "a b ", "-x' -y"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + options := GenerateBindingsOptions{ + BuildFlagsString: tt.input, + } + + var wantErr error = nil + if tt.wantErr { + wantErr = ErrUnmatchedQuote + } + + gotFlags, gotErr := options.BuildFlags() + + if diff := cmp.Diff(tt.wantFlags, gotFlags); diff != "" { + t.Errorf("BuildFlags() unexpected result: %s\n", diff) + } + + if diff := cmp.Diff(wantErr, gotErr, cmpopts.EquateErrors()); diff != "" { + t.Errorf("BuildFlags() unexpected error: %s\n", diff) + } + }) + } +} diff --git a/v3/internal/flags/common.go b/v3/internal/flags/common.go new file mode 100644 index 000000000..e58eff411 --- /dev/null +++ b/v3/internal/flags/common.go @@ -0,0 +1,5 @@ +package flags + +type Common struct { + NoColour bool `description:"Disable colour in output"` +} diff --git a/v3/internal/flags/init.go b/v3/internal/flags/init.go new file mode 100644 index 000000000..df6baa56c --- /dev/null +++ b/v3/internal/flags/init.go @@ -0,0 +1,22 @@ +package flags + +type Init struct { + Common + + PackageName string `name:"p" description:"Package name" default:"main"` + TemplateName string `name:"t" description:"Name of built-in template to use, path to template or template url" default:"vanilla"` + ProjectName string `name:"n" description:"Name of project" default:""` + ProjectDir string `name:"d" description:"Project directory" default:"."` + Quiet bool `name:"q" description:"Suppress output to console"` + List bool `name:"l" description:"List templates"` + SkipGoModTidy bool `name:"skipgomodtidy" description:"Skip running go mod tidy"` + Git string `name:"git" description:"Git repository URL to initialize (e.g. github.com/username/project)"` + ProductName string `description:"The name of the product" default:"My Product"` + ProductDescription string `description:"The description of the product" default:"My Product Description"` + ProductVersion string `description:"The version of the product" default:"0.1.0"` + ProductCompany string `description:"The company of the product" default:"My Company"` + ProductCopyright string `description:"The copyright notice" default:"\u00a9 now, My Company"` + ProductComments string `description:"Comments to add to the generated files" default:"This is a comment"` + ProductIdentifier string `description:"The product identifier, e.g com.mycompany.myproduct"` + SkipWarning bool `name:"s" description:"Skips the warning message when using remote templates"` +} diff --git a/v3/internal/flags/msix.go b/v3/internal/flags/msix.go new file mode 100644 index 000000000..17bbbd446 --- /dev/null +++ b/v3/internal/flags/msix.go @@ -0,0 +1,26 @@ +package flags + +// ToolMSIX represents the options for the MSIX packaging command +type ToolMSIX struct { + Common + + // Project configuration + ConfigPath string `name:"config" description:"Path to the project configuration file" default:"wails.json"` + + // MSIX package information + Publisher string `name:"publisher" description:"Publisher name for the MSIX package (e.g., CN=CompanyName)" default:""` + + // Certificate for signing + CertificatePath string `name:"cert" description:"Path to the certificate file for signing the MSIX package" default:""` + CertificatePassword string `name:"cert-password" description:"Password for the certificate file" default:""` + + // Build options + Arch string `name:"arch" description:"Architecture of the package (x64, x86, arm64)" default:"x64"` + ExecutableName string `name:"name" description:"Name of the executable in the package" default:""` + ExecutablePath string `name:"executable" description:"Path to the executable file to package" default:""` + OutputPath string `name:"out" description:"Path where the MSIX package will be saved" default:""` + + // Tool selection + UseMsixPackagingTool bool `name:"use-msix-tool" description:"Use the Microsoft MSIX Packaging Tool for packaging" default:"false"` + UseMakeAppx bool `name:"use-makeappx" description:"Use MakeAppx.exe for packaging" default:"true"` +} diff --git a/v3/internal/flags/package.go b/v3/internal/flags/package.go new file mode 100644 index 000000000..bd56107c5 --- /dev/null +++ b/v3/internal/flags/package.go @@ -0,0 +1,13 @@ +package flags + +// ToolPackage represents the options for the package command +type ToolPackage struct { + Common + + Format string `name:"format" description:"Package format to generate (deb, rpm, archlinux, dmg)" default:"deb"` + ExecutableName string `name:"name" description:"Name of the executable to package" default:"myapp"` + ConfigPath string `name:"config" description:"Path to the package configuration file" default:""` + Out string `name:"out" description:"Path to the output dir" default:"."` + BackgroundImage string `name:"background" description:"Path to an optional background image for the DMG" default:""` + CreateDMG bool `name:"create-dmg" description:"Create a DMG file (macOS only)" default:"false"` +} diff --git a/v3/internal/flags/service.go b/v3/internal/flags/service.go new file mode 100644 index 000000000..f7fd8ec2e --- /dev/null +++ b/v3/internal/flags/service.go @@ -0,0 +1,14 @@ +package flags + +type ServiceInit struct { + Name string `name:"n" description:"Name of service" default:"example_service"` + Description string `name:"d" description:"Description of service" default:"Example service"` + PackageName string `name:"p" description:"Package name for service" default:""` + OutputDir string `name:"o" description:"Output directory" default:"."` + Quiet bool `name:"q" description:"Suppress output to console"` + Author string `name:"a" description:"Author of service" default:""` + Version string `name:"v" description:"Version of service" default:""` + Website string `name:"w" description:"Website of service" default:""` + Repository string `name:"r" description:"Repository of service" default:""` + License string `name:"l" description:"License of service" default:""` +} diff --git a/v3/internal/flags/task_wrapper.go b/v3/internal/flags/task_wrapper.go new file mode 100644 index 000000000..4a6ade362 --- /dev/null +++ b/v3/internal/flags/task_wrapper.go @@ -0,0 +1,13 @@ +package flags + +type Build struct { + Common +} + +type Dev struct { + Common +} + +type Package struct { + Common +} diff --git a/v3/internal/generator/.gitignore b/v3/internal/generator/.gitignore new file mode 100644 index 000000000..3ed83544e --- /dev/null +++ b/v3/internal/generator/.gitignore @@ -0,0 +1,4 @@ +.task +node_modules +testdata/output/**/*.got.[jt]s +testdata/output/**/*.got.log diff --git a/v3/internal/generator/README.md b/v3/internal/generator/README.md new file mode 100644 index 000000000..20b879245 --- /dev/null +++ b/v3/internal/generator/README.md @@ -0,0 +1,6 @@ +# Generator + +This package contains the static analyser used for parsing Wails projects so that we may: + +- Generate the bindings for the frontend +- Generate Typescript definitions for the structs used by the bindings diff --git a/v3/internal/generator/Taskfile.yaml b/v3/internal/generator/Taskfile.yaml new file mode 100644 index 000000000..491e426b9 --- /dev/null +++ b/v3/internal/generator/Taskfile.yaml @@ -0,0 +1,56 @@ +# https://taskfile.dev + +version: "3" + +shopt: [globstar] + +tasks: + clean: + cmds: + - rm -rf ./testdata/output/**/*.got.[jt]s ./testdata/output/**/*.got.log + + test: + cmds: + - go test -count=1 -v . + - task: test:check + + test:analyse: + cmds: + - go test -count=1 -v -run ^TestAnalyser . + + test:constants: + cmds: + - go test -v -count=1 -run ^TestGenerateConstants . + + test:generate: + cmds: + - go test -v -count=1 -run ^TestGenerator . + - task: test:check + + test:regenerate: + cmds: + - cmd: rm -rf ./testdata/output/* + - cmd: go test -v -count=1 -run ^TestGenerator . + ignore_error: true + - task: test:generate + + test:check: + dir: ./testdata + deps: + - install-deps + cmds: + - npx tsc + - npx madge --circular output/ + + install-deps: + internal: true + dir: ./testdata + sources: + - package.json + cmds: + - npm install + + update: + dir: ./testdata + cmds: + - npx npm-check-updates -u diff --git a/v3/internal/generator/analyse.go b/v3/internal/generator/analyse.go new file mode 100644 index 000000000..411e6ff4f --- /dev/null +++ b/v3/internal/generator/analyse.go @@ -0,0 +1,202 @@ +package generator + +import ( + "fmt" + "go/token" + "go/types" + "iter" + + "github.com/wailsapp/wails/v3/internal/generator/config" + "golang.org/x/tools/go/packages" +) + +// FindServices scans the given packages for invocations +// of the NewService function from the Wails application package. +// +// Whenever one is found and the type of its unique argument +// is a valid service type, the corresponding named type object +// is passed to yield. +// +// Results are deduplicated, i.e. yield is called at most once per object. +// +// If yield returns false, FindBoundTypes returns immediately. +func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, logger config.Logger) (iter.Seq[*types.TypeName], error) { + type instanceInfo struct { + args *types.TypeList + pos token.Position + } + + type target struct { + obj types.Object + param int + } + + type targetInfo struct { + target + cause token.Position + } + + // instances maps objects (TypeName or Func) to their instance list. + instances := make(map[types.Object][]instanceInfo) + + // owner maps type parameter objects to their parent object (TypeName or Func) + owner := make(map[*types.TypeName]types.Object) + + // scheduled holds the set of type parameters + // that have been already scheduled for analysis, + // for deduplication. + scheduled := make(map[target]bool) + + // next lists type parameter objects that have yet to be analysed. + var next []targetInfo + + // Initialise instance/owner maps and detect application.NewService. + for _, pkg := range pkgs { + for ident, instance := range pkg.TypesInfo.Instances { + obj := pkg.TypesInfo.Uses[ident] + + // Add to instance map. + objInstances, seen := instances[obj] + instances[obj] = append(objInstances, instanceInfo{ + instance.TypeArgs, + pkg.Fset.Position(ident.Pos()), + }) + + if seen { + continue + } + + // Object seen for the first time: + // add type params to owner map. + var tp *types.TypeParamList + + if t, ok := obj.Type().(interface{ TypeParams() *types.TypeParamList }); ok { + tp = t.TypeParams() + } else { + // Instantiated object has unexpected kind: + // the spec might have changed. + logger.Warningf( + "unexpected instantiation for %s: please report this to Wails maintainers", + types.ObjectString(obj, nil), + ) + continue + } + + // Add type params to owner map. + for i := range tp.Len() { + if param := tp.At(i).Obj(); param != nil { + owner[param] = obj + } + } + + // If this is a named type, process methods. + if recv, ok := obj.Type().(*types.Named); ok && recv.NumMethods() > 0 { + // Register receiver type params. + for i := range recv.NumMethods() { + tp := recv.Method(i).Type().(*types.Signature).RecvTypeParams() + for j := range tp.Len() { + if param := tp.At(j).Obj(); param != nil { + owner[param] = obj + } + } + } + } + + if len(next) > 0 { + // application.NewService has been found already. + continue + } + + fn, ok := obj.(*types.Func) + if !ok { + continue + } + + // Detect application.NewService + if fn.Name() == "NewService" && fn.Pkg().Path() == systemPaths.ApplicationPackage { + // Check signature. + signature := fn.Type().(*types.Signature) + if signature.Params().Len() > 2 || signature.Results().Len() != 1 || tp.Len() != 1 || tp.At(0).Obj() == nil { + logger.Warningf("Param Len: %d, Results Len: %d, tp.Len: %d, tp.At(0).Obj(): %v", signature.Params().Len(), signature.Results().Len(), tp.Len(), tp.At(0).Obj()) + return nil, ErrBadApplicationPackage + } + + // Schedule unique type param for analysis. + tgt := target{obj, 0} + scheduled[tgt] = true + next = append(next, targetInfo{target: tgt}) + } + } + } + + // found tracks service types that have been found so far, for deduplication. + found := make(map[*types.TypeName]bool) + + return func(yield func(*types.TypeName) bool) { + // Process targets. + for len(next) > 0 { + // Pop one target off the next list. + tgt := next[len(next)-1] + next = next[:len(next)-1] + + // Prepare indirect binding message. + indirectMsg := "" + if tgt.cause.IsValid() { + indirectMsg = fmt.Sprintf(" (indirectly bound at %s)", tgt.cause) + } + + for _, instance := range instances[tgt.obj] { + // Retrieve type argument. + serviceType := types.Unalias(instance.args.At(tgt.param)) + + var named *types.Named + + switch t := serviceType.(type) { + case *types.Named: + // Process named type. + named = t.Origin() + + case *types.TypeParam: + // Schedule type parameter for analysis. + newtgt := target{owner[t.Obj()], t.Index()} + if !scheduled[newtgt] { + scheduled[newtgt] = true + + // Retrieve position of call to application.NewService + // that caused this target to be scheduled. + cause := tgt.cause + if !tgt.cause.IsValid() { + // This _is_ a call to application.NewService. + cause = instance.pos + } + + // Push on next list. + next = append(next, targetInfo{newtgt, cause}) + } + continue + + default: + logger.Warningf("%s: ignoring anonymous service type %s%s", instance.pos, serviceType, indirectMsg) + continue + } + + // Reject interfaces and generic types. + if types.IsInterface(named.Underlying()) { + logger.Warningf("%s: ignoring interface service type %s%s", instance.pos, named, indirectMsg) + continue + } else if named.TypeParams() != nil { + logger.Warningf("%s: ignoring generic service type %s", instance.pos, named, indirectMsg) + continue + } + + // Record and yield type object. + if !found[named.Obj()] { + found[named.Obj()] = true + if !yield(named.Obj()) { + return + } + } + } + } + }, nil +} diff --git a/v3/internal/generator/analyse_test.go b/v3/internal/generator/analyse_test.go new file mode 100644 index 000000000..576e56f6f --- /dev/null +++ b/v3/internal/generator/analyse_test.go @@ -0,0 +1,116 @@ +package generator + +import ( + "encoding/json" + "errors" + "go/types" + "os" + "path" + "path/filepath" + "slices" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/pterm/pterm" + "github.com/wailsapp/wails/v3/internal/generator/config" +) + +func TestAnalyser(t *testing.T) { + type testParams struct { + name string + want []string + } + + // Gather tests from cases directory. + entries, err := os.ReadDir("testcases") + if err != nil { + t.Fatal(err) + } + + tests := make([]testParams, 0, len(entries)+1) + + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + test := testParams{ + name: entry.Name(), + want: make([]string, 0), + } + + want, err := os.Open(filepath.Join("testcases", entry.Name(), "bound_types.json")) + if err != nil { + if !errors.Is(err, os.ErrNotExist) { + t.Fatal(err) + } + } else { + err = json.NewDecoder(want).Decode(&test.want) + want.Close() + if err != nil { + t.Fatal(err) + } + } + + for i := range test.want { + test.want[i] = path.Clean("github.com/wailsapp/wails/v3/internal/generator/testcases/" + test.name + test.want[i]) + } + slices.Sort(test.want) + + tests = append(tests, test) + } + + // Add global test. + { + all := testParams{ + name: "...", + } + + for _, test := range tests { + all.want = append(all.want, test.want...) + } + slices.Sort(all.want) + + tests = append(tests, all) + } + + // Resolve system package paths. + systemPaths, err := ResolveSystemPaths(nil) + if err != nil { + t.Fatal(err) + } + + for _, test := range tests { + pkgPattern := "github.com/wailsapp/wails/v3/internal/generator/testcases/" + test.name + + t.Run("pkg="+test.name, func(t *testing.T) { + pkgs, err := LoadPackages(nil, pkgPattern) + if err != nil { + t.Fatal(err) + } + + for _, pkg := range pkgs { + for _, err := range pkg.Errors { + pterm.Warning.Println(err) + } + } + + got := make([]string, 0) + + services, err := FindServices(pkgs, systemPaths, config.DefaultPtermLogger(nil)) + if err != nil { + t.Error(err) + } + + for obj := range services { + got = append(got, types.TypeString(obj.Type(), nil)) + } + + slices.Sort(got) + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("Found services mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/v3/internal/generator/collect/_reference/json_marshaler_behaviour.go b/v3/internal/generator/collect/_reference/json_marshaler_behaviour.go new file mode 100644 index 000000000..bc2c99524 --- /dev/null +++ b/v3/internal/generator/collect/_reference/json_marshaler_behaviour.go @@ -0,0 +1,166 @@ +// This example explores exhaustively the behaviour of encoding/json +// when handling types that implement marshaler interfaces. +// +// When encoding values, encoding/json makes no distinction +// between pointer and base types: +// if the base type implements a marshaler interface, +// be it with plain receiver or pointer receiver, +// json.Marshal picks it up. +// +// json.Marshaler is always preferred to encoding.TextMarshaler, +// without any consideration for the receiver type. +// +// When encoding map keys, on the other hand, +// the map key type must implement encoding.TextMarshaler strictly, +// i.e. if the interface is implemented only for plain receivers, +// pointer keys will not be accepted. +// +// json.Marshaler is ignored in this case, i.e. it makes no difference +// whether the key type implements it or not. +// +// Decoding behaviour w.r.t. unmarshaler types mirrors encoding behaviour. +package main + +import ( + "encoding/json" + "fmt" +) + +type A struct{} +type B struct{} +type C struct{} +type D struct{} +type E struct{} +type F struct{} +type G struct{} +type H struct{} + +type T struct { + A A + Ap *A + B B + Bp *B + C C + Cp *C + D D + Dp *D + E E + Ep *E + F F + Fp *F + G G + Gp *G + H H + Hp *H +} + +type MT struct { + //A map[A]bool // error + //Ap map[*A]bool // error + //B map[B]bool // error + //Bp map[*B]bool // error + C map[C]bool + Cp map[*C]bool + //D map[D]bool // error + Dp map[*D]bool + E map[E]bool + Ep map[*E]bool + //F map[F]bool // error + Fp map[*F]bool + G map[G]bool + Gp map[*G]bool + //H map[H]bool // error + Hp map[*H]bool +} + +func (A) MarshalJSON() ([]byte, error) { + return []byte(`"This is j A"`), nil +} + +func (*B) MarshalJSON() ([]byte, error) { + return []byte(`"This is j *B"`), nil +} + +func (C) MarshalText() ([]byte, error) { + return []byte(`This is t C`), nil +} + +func (*D) MarshalText() ([]byte, error) { + return []byte(`This is t *D`), nil +} + +func (E) MarshalJSON() ([]byte, error) { + return []byte(`"This is j E"`), nil +} + +func (E) MarshalText() ([]byte, error) { + return []byte(`This is t E`), nil +} + +func (F) MarshalJSON() ([]byte, error) { + return []byte(`"This is j F"`), nil +} + +func (*F) MarshalText() ([]byte, error) { + return []byte(`This is t *F`), nil +} + +func (*G) MarshalJSON() ([]byte, error) { + return []byte(`"This is j *G"`), nil +} + +func (G) MarshalText() ([]byte, error) { + return []byte(`This is t G`), nil +} + +func (*H) MarshalJSON() ([]byte, error) { + return []byte(`"This is j *H"`), nil +} + +func (*H) MarshalText() ([]byte, error) { + return []byte(`This is t *H`), nil +} + +func main() { + t := &T{ + A{}, + &A{}, + B{}, + &B{}, + C{}, + &C{}, + D{}, + &D{}, + E{}, + &E{}, + F{}, + &F{}, + G{}, + &G{}, + H{}, + &H{}, + } + enc, err := json.Marshal(t) + fmt.Println(string(enc), err) + + mt := &MT{ + //map[A]bool{A{}: true}, // error + //map[*A]bool{&A{}: true}, // error + //map[B]bool{B{}: true}, // error + //map[*B]bool{&B{}: true}, // error + map[C]bool{C{}: true}, + map[*C]bool{&C{}: true}, + //map[D]bool{D{}: true}, // error + map[*D]bool{&D{}: true}, + map[E]bool{E{}: true}, + map[*E]bool{&E{}: true}, + //map[F]bool{F{}: true}, // error + map[*F]bool{&F{}: true}, + map[G]bool{G{}: true}, + map[*G]bool{&G{}: true}, + //map[H]bool{H{}: true}, // error + map[*H]bool{&H{}: true}, + } + enc, err = json.Marshal(mt) + fmt.Println(string(enc), err) +} diff --git a/v3/internal/generator/collect/collector.go b/v3/internal/generator/collect/collector.go new file mode 100644 index 000000000..e4acd53e8 --- /dev/null +++ b/v3/internal/generator/collect/collector.go @@ -0,0 +1,108 @@ +package collect + +import ( + "go/ast" + "go/types" + "sync" + + "github.com/wailsapp/wails/v3/internal/flags" + "github.com/wailsapp/wails/v3/internal/generator/config" + "golang.org/x/tools/go/packages" +) + +// Scheduler instances provide task scheduling +// for collection activities. +type Scheduler interface { + // Schedule should run the given function according + // to the implementation's preferred strategy. + // + // Scheduled tasks may call Schedule again; + // therefore, if tasks run concurrently, + // the implementation must support concurrent calls. + Schedule(task func()) +} + +// Info instances provide information about either +// a type-checker object, a struct type or a group of declarations. +type Info interface { + Object() types.Object + Type() types.Type + Node() ast.Node +} + +// Collector wraps all bookkeeping data structures that are needed +// to collect data about a set of packages, bindings and models. +type Collector struct { + // pkgs caches packages that have been registered for collection. + pkgs map[*types.Package]*PackageInfo + + // cache caches collected information about type-checker objects + // and declaration groups. Elements are [Info] instances. + cache sync.Map + + systemPaths *config.SystemPaths + options *flags.GenerateBindingsOptions + scheduler Scheduler + logger config.Logger +} + +// NewCollector initialises a new Collector instance for the given package set. +func NewCollector(pkgs []*packages.Package, systemPaths *config.SystemPaths, options *flags.GenerateBindingsOptions, scheduler Scheduler, logger config.Logger) *Collector { + collector := &Collector{ + pkgs: make(map[*types.Package]*PackageInfo, len(pkgs)), + + systemPaths: systemPaths, + options: options, + scheduler: scheduler, + logger: logger, + } + + // Register packages. + for _, pkg := range pkgs { + collector.pkgs[pkg.Types] = newPackageInfo(pkg, collector) + } + + return collector +} + +// fromCache returns the cached Info instance associated +// to the given type-checker object or declaration group. +// If none exists, a new one is created. +func (collector *Collector) fromCache(objectOrGroup any) Info { + entry, ok := collector.cache.Load(objectOrGroup) + info, _ := entry.(Info) + + if !ok { + switch x := objectOrGroup.(type) { + case *ast.GenDecl, *ast.ValueSpec, *ast.Field: + info = newGroupInfo(x.(ast.Node)) + + case *types.Const: + info = newConstInfo(collector, x) + + case *types.Func: + info = newMethodInfo(collector, x.Origin()) + + case *types.TypeName: + info = newTypeInfo(collector, x) + + case *types.Var: + if !x.IsField() { + panic("cache lookup for invalid object kind") + } + + info = newFieldInfo(collector, x.Origin()) + + case *types.Struct: + info = newStructInfo(collector, x) + + default: + panic("cache lookup for invalid object kind") + } + + entry, _ = collector.cache.LoadOrStore(objectOrGroup, info) + info, _ = entry.(Info) + } + + return info +} diff --git a/v3/internal/generator/collect/const.go b/v3/internal/generator/collect/const.go new file mode 100644 index 000000000..ad624bdad --- /dev/null +++ b/v3/internal/generator/collect/const.go @@ -0,0 +1,104 @@ +package collect + +import ( + "go/ast" + "go/constant" + "go/token" + "go/types" + "sync" +) + +// ConstInfo records information about a constant declaration. +// +// Read accesses to any public field are only safe +// if a call to [ConstInfo.Collect] has completed before the access, +// for example by calling it in the accessing goroutine +// or before spawning the accessing goroutine. +type ConstInfo struct { + Name string + Value any + + Pos token.Pos + Spec *GroupInfo + Decl *GroupInfo + + obj *types.Const + node ast.Node + + collector *Collector + once sync.Once +} + +func newConstInfo(collector *Collector, obj *types.Const) *ConstInfo { + return &ConstInfo{ + obj: obj, + collector: collector, + } +} + +// Const returns the unique ConstInfo instance +// associated to the given object within a collector. +// +// Const is safe for concurrent use. +func (collector *Collector) Const(obj *types.Const) *ConstInfo { + return collector.fromCache(obj).(*ConstInfo) +} + +func (info *ConstInfo) Object() types.Object { + return info.obj +} + +func (info *ConstInfo) Type() types.Type { + return info.obj.Type() +} + +func (info *ConstInfo) Node() ast.Node { + return info.Collect().node +} + +// Collect gathers information about the constant described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *ConstInfo) Collect() *ConstInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + collector := info.collector + + info.Name = info.obj.Name() + info.Value = constant.Val(info.obj.Val()) + + info.Pos = info.obj.Pos() + + path := collector.findDeclaration(info.obj) + if path == nil { + collector.logger.Warningf( + "package %s: const %s: could not find declaration for constant object", + info.obj.Pkg().Path(), + info.Name, + ) + + // Provide dummy groups. + dummyGroup := newGroupInfo(nil).Collect() + info.Spec = dummyGroup + info.Decl = dummyGroup + return + } + + // path shape: *ast.ValueSpec, *ast.GenDecl, *ast.File + info.Spec = collector.fromCache(path[0]).(*GroupInfo).Collect() + info.Decl = collector.fromCache(path[1]).(*GroupInfo).Collect() + info.node = path[0] + }) + + return info +} diff --git a/v3/internal/generator/collect/declaration.go b/v3/internal/generator/collect/declaration.go new file mode 100644 index 000000000..e592b5b1a --- /dev/null +++ b/v3/internal/generator/collect/declaration.go @@ -0,0 +1,238 @@ +package collect + +import ( + "cmp" + "go/ast" + "go/token" + "go/types" + "slices" +) + +// findDeclaration returns the AST spec or declaration +// that defines the given _global_ type-checker object. +// +// Specifically, the first element in the returned slice +// is the relevant spec or declaration, followed by its chain +// of parent nodes up to the declaring [ast.File]. +// +// If no corresponding declaration can be found within +// the set of registered packages, the returned slice is nil. +// +// Resulting node types are as follows: +// - global functions and concrete methods (*types.Func) +// map to *ast.FuncDecl nodes; +// - interface methods from global interfaces (*types.Func) +// map to *ast.Field nodes within their interface expression; +// - struct fields from global structs (*types.Var) +// map to *ast.Field nodes within their struct expression; +// - global constants and variables map to *ast.ValueSpec nodes; +// - global named types map to *ast.TypeSpec nodes; +// - for type parameters, the result is always nil; +// - for local objects defined within functions, +// field types, variable types or field values, +// the result is always nil; +// +// findDeclaration supports unsynchronised concurrent calls. +func (collector *Collector) findDeclaration(obj types.Object) (path []ast.Node) { + pkg := collector.Package(obj.Pkg()).Collect() + if pkg == nil { + return nil + } + + // Perform a binary search to find the file enclosing the node. + // We can't use findEnclosingNode here because it is less accurate and less efficient with files. + fileIndex, exact := slices.BinarySearchFunc(pkg.Files, obj.Pos(), func(f *ast.File, p token.Pos) int { + return cmp.Compare(f.FileStart, p) + }) + + // If exact is true, pkg.Files[fileIndex] is the file we are looking for; + // otherwise, it is the first file whose start position is _after_ obj.Pos(). + if !exact { + fileIndex-- + } + + // When exact is false, the position might lie within an empty segment in between two files. + if fileIndex < 0 || pkg.Files[fileIndex].FileEnd <= obj.Pos() { + return nil + } + + file := pkg.Files[fileIndex] + + // Find enclosing declaration. + decl := findEnclosingNode(file.Decls, obj.Pos()) + if decl == nil { + // Invalid position. + return nil + } + + var gen *ast.GenDecl + + switch d := decl.(type) { + case *ast.FuncDecl: + if obj.Pos() == d.Name.Pos() { + // Object is function. + return []ast.Node{decl, file} + } + + // Ignore local objects defined within function bodies. + return nil + + case *ast.BadDecl: + // What's up?? + return nil + + case *ast.GenDecl: + gen = d + } + + // Handle *ast.GenDecl + + // Find enclosing ast.Spec + spec := findEnclosingNode(gen.Specs, obj.Pos()) + if spec == nil { + // Invalid position. + return nil + } + + var def ast.Expr + + switch s := spec.(type) { + case *ast.ValueSpec: + if s.Names[0].Pos() <= obj.Pos() && obj.Pos() < s.Names[len(s.Names)-1].End() { + // Object is variable or constant. + return []ast.Node{spec, decl, file} + } + + // Ignore local objects defined within variable types/values. + return nil + + case *ast.TypeSpec: + if obj.Pos() == s.Name.Pos() { + // Object is named type. + return []ast.Node{spec, decl, file} + } + + if obj.Pos() < s.Type.Pos() || s.Type.End() <= obj.Pos() { + // Type param or invalid position. + return nil + } + + // Struct or interface field? + def = s.Type + } + + // Handle struct or interface field. + + var iface *ast.InterfaceType + + switch d := def.(type) { + case *ast.StructType: + // Find enclosing field + field := findEnclosingNode(d.Fields.List, obj.Pos()) + if field == nil { + // Invalid position. + return nil + } + + if len(field.Names) == 0 { + // Handle embedded field. + ftype := ast.Unparen(field.Type) + + // Unwrap pointer. + if ptr, ok := ftype.(*ast.StarExpr); ok { + ftype = ast.Unparen(ptr.X) + } + + // Unwrap generic instantiation. + switch t := field.Type.(type) { + case *ast.IndexExpr: + ftype = ast.Unparen(t.X) + case *ast.IndexListExpr: + ftype = ast.Unparen(t.X) + } + + // Unwrap selector. + if sel, ok := ftype.(*ast.SelectorExpr); ok { + ftype = sel.Sel + } + + // ftype must now be an identifier. + if obj.Pos() == ftype.Pos() { + // Object is this embedded field. + return []ast.Node{field, d.Fields, def, spec, decl, file} + } + } else if field.Names[0].Pos() <= obj.Pos() && obj.Pos() < field.Names[len(field.Names)-1].End() { + // Object is one of these fields. + return []ast.Node{field, d.Fields, def, spec, decl, file} + } + + // Ignore local objects defined within field types. + return nil + + case *ast.InterfaceType: + iface = d + + default: + // Other local object or invalid position. + return nil + } + + path = []ast.Node{file, decl, spec, def, iface.Methods} + + // Handle interface method. + for { + field := findEnclosingNode(iface.Methods.List, obj.Pos()) + if field == nil { + // Invalid position. + return nil + } + + path = append(path, field) + + if len(field.Names) == 0 { + // Handle embedded interface. + var ok bool + iface, ok = ast.Unparen(field.Type).(*ast.InterfaceType) + if !ok { + // Not embedded interface, ignore. + return nil + } + + path = append(path, iface, iface.Methods) + // Explore embedded interface. + + } else if field.Names[0].Pos() <= obj.Pos() && obj.Pos() < field.Names[len(field.Names)-1].End() { + // Object is one of these fields. + slices.Reverse(path) + return path + } else { + // Ignore local objects defined within interface method signatures. + return nil + } + } +} + +// findEnclosingNode finds the unique node in nodes, if any, +// that encloses the given position. +// +// It uses binary search and therefore expects +// the nodes slice to be sorted in source order. +func findEnclosingNode[S ~[]E, E ast.Node](nodes S, pos token.Pos) (node E) { + // Perform a binary search to find the nearest node. + index, exact := slices.BinarySearchFunc(nodes, pos, func(n E, p token.Pos) int { + return cmp.Compare(n.Pos(), p) + }) + + // If exact is true, nodes[index] is the node we are looking for; + // otherwise, it is the first node whose start position is _after_ pos. + if !exact { + index-- + } + + // When exact is false, the position might lie within an empty segment in between two nodes. + if index < 0 || nodes[index].End() <= pos { + return // zero value, nil in practice. + } + + return nodes[index] +} diff --git a/v3/internal/generator/collect/directive.go b/v3/internal/generator/collect/directive.go new file mode 100644 index 000000000..05293a640 --- /dev/null +++ b/v3/internal/generator/collect/directive.go @@ -0,0 +1,101 @@ +package collect + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" + + "github.com/wailsapp/wails/v3/internal/flags" +) + +// IsDirective returns true if the given comment +// is a directive of the form //wails: + directive. +func IsDirective(comment string, directive string) bool { + if strings.HasPrefix(comment, "//wails:"+directive) { + length := len("//wails:") + len(directive) + if len(comment) == length { + return true + } + + next, _ := utf8.DecodeRuneInString(comment[length:]) + return unicode.IsSpace(next) + } + + return false +} + +// ParseDirective extracts the argument portion of a //wails: + directive comment. +func ParseDirective(comment string, directive string) string { + rawArg := comment[len("//wails:")+len(directive):] + + if directive != "inject" { + return strings.TrimSpace(rawArg) + } + + // wails:inject requires special parsing: + // do not trim all surrounding space, just the one space + // immediately after the directive name. + _, wsize := utf8.DecodeRuneInString(rawArg) + return rawArg[wsize:] +} + +// ParseCondition parses an optional two-character condition prefix +// for include or inject directives. +// It returns the argument stripped of the prefix and the resulting condition. +// If the condition is malformed, ParseCondition returns a non-nil error. +func ParseCondition(argument string) (string, Condition, error) { + cond, arg, present := strings.Cut(argument, ":") + if !present { + return cond, Condition{true, true, true, true}, nil + } + + if len(cond) != 2 || !strings.ContainsRune("*jt", rune(cond[0])) || !strings.ContainsRune("*ci", rune(cond[1])) { + return argument, + Condition{true, true, true, true}, + fmt.Errorf("invalid condition code '%s': expected format is '(*|j|t)(*|c|i)'", cond) + } + + condition := Condition{true, true, true, true} + + switch cond[0] { + case 'j': + condition.TS = false + case 't': + condition.JS = false + } + + switch cond[1] { + case 'c': + condition.Interfaces = false + case 'i': + condition.Classes = false + } + + return arg, condition, nil +} + +type Condition struct { + JS bool + TS bool + Classes bool + Interfaces bool +} + +// Satisfied returns true when the condition described by the receiver +// is satisfied by the given configuration. +func (cond Condition) Satisfied(options *flags.GenerateBindingsOptions) bool { + if options.TS { + if options.UseInterfaces { + return cond.TS && cond.Interfaces + } else { + return cond.TS && cond.Classes + } + } else { + if options.UseInterfaces { + return cond.JS && cond.Interfaces + } else { + return cond.JS && cond.Classes + } + } +} diff --git a/v3/internal/generator/collect/field.go b/v3/internal/generator/collect/field.go new file mode 100644 index 000000000..eed808114 --- /dev/null +++ b/v3/internal/generator/collect/field.go @@ -0,0 +1,102 @@ +package collect + +import ( + "go/ast" + "go/token" + "go/types" + "sync" +) + +// FieldInfo records information about a struct field declaration. +// +// Read accesses to any public field are only safe +// if a call to [FieldInfo.Collect] has completed before the access, +// for example by calling it in the accessing goroutine +// or before spawning the accessing goroutine. +type FieldInfo struct { + Name string + Blank bool + Embedded bool + + Pos token.Pos + Decl *GroupInfo + + obj *types.Var + node ast.Node + + collector *Collector + once sync.Once +} + +// newFieldInfo initialises a descriptor for the given field object. +func newFieldInfo(collector *Collector, obj *types.Var) *FieldInfo { + return &FieldInfo{ + obj: obj, + collector: collector, + } +} + +// Field returns the unique FieldInfo instance +// associated to the given object within a collector. +// +// Field is safe for concurrent use. +func (collector *Collector) Field(obj *types.Var) *FieldInfo { + if !obj.IsField() { + return nil + } + + return collector.fromCache(obj).(*FieldInfo) +} + +func (info *FieldInfo) Object() types.Object { + return info.obj +} + +func (info *FieldInfo) Type() types.Type { + return info.obj.Type() +} + +func (info *FieldInfo) Node() ast.Node { + return info.Collect().node +} + +// Collect gathers information about the struct field +// described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *FieldInfo) Collect() *FieldInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + collector := info.collector + + info.Name = info.obj.Name() + info.Blank = (info.Name == "" || info.Name == "_") + info.Embedded = info.obj.Embedded() + + info.Pos = info.obj.Pos() + + path := collector.findDeclaration(info.obj) + if path == nil { + // Do not report failure: it is expected for anonymous struct fields. + // Provide dummy group. + info.Decl = newGroupInfo(nil).Collect() + return + } + + // path shape: *ast.Field, *ast.FieldList, ... + info.Decl = collector.fromCache(path[0]).(*GroupInfo).Collect() + info.node = path[0] + }) + + return info +} diff --git a/v3/internal/generator/collect/group.go b/v3/internal/generator/collect/group.go new file mode 100644 index 000000000..62d83304c --- /dev/null +++ b/v3/internal/generator/collect/group.go @@ -0,0 +1,95 @@ +package collect + +import ( + "go/ast" + "go/token" + "go/types" + "slices" + "sync" +) + +// GroupInfo records information about a group +// of type, field or constant declarations. +// This may be either a list of distinct specifications +// wrapped in parentheses, or a single specification +// declaring multiple fields or constants. +// +// Read accesses to any public field are only safe +// if a call to [GroupInfo.Collect] has completed before the access, +// for example by calling it in the accessing goroutine +// or before spawning the accessing goroutine. +type GroupInfo struct { + Pos token.Pos + Doc *ast.CommentGroup + + node ast.Node + + once sync.Once +} + +func newGroupInfo(node ast.Node) *GroupInfo { + return &GroupInfo{ + node: node, + } +} + +func (*GroupInfo) Object() types.Object { + return nil +} + +func (*GroupInfo) Type() types.Type { + return nil +} + +func (info *GroupInfo) Node() ast.Node { + return info.node +} + +// Collect gathers information about the declaration group +// described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *GroupInfo) Collect() *GroupInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + switch n := info.node.(type) { + case *ast.GenDecl: + info.Pos = n.Pos() + info.Doc = n.Doc + + case *ast.ValueSpec: + info.Pos = n.Pos() + info.Doc = n.Doc + if info.Doc == nil { + info.Doc = n.Comment + } else if n.Comment != nil { + info.Doc = &ast.CommentGroup{ + List: slices.Concat(n.Doc.List, n.Comment.List), + } + } + + case *ast.Field: + info.Pos = n.Pos() + info.Doc = n.Doc + if info.Doc == nil { + info.Doc = n.Comment + } else if n.Comment != nil { + info.Doc = &ast.CommentGroup{ + List: slices.Concat(n.Doc.List, n.Comment.List), + } + } + } + }) + + return info +} diff --git a/v3/internal/generator/collect/imports.go b/v3/internal/generator/collect/imports.go new file mode 100644 index 000000000..b62e53963 --- /dev/null +++ b/v3/internal/generator/collect/imports.go @@ -0,0 +1,283 @@ +package collect + +import ( + "go/types" + "path/filepath" + + "golang.org/x/tools/go/types/typeutil" +) + +type ( + // ImportMap records deduplicated imports by a binding or models module. + // It computes relative import paths and assigns import names, + // taking care to avoid collisions. + ImportMap struct { + // Self records the path of the importing package. + Self string + + // ImportModels records whether models from the current package may be needed. + ImportModels bool + + // External records information about each imported package, + // keyed by package path. + External map[string]ImportInfo + + // counters holds the occurence count for each package name in External. + counters map[string]int + collector *Collector + } + + // ImportInfo records information about a single import. + ImportInfo struct { + Name string + Index int // Progressive number for identically named imports, starting from 0 for each distinct name. + RelPath string + } +) + +// NewImportMap initialises an import map for the given importer package. +// The argument may be nil, in which case import paths will be relative +// to the root output directory. +func NewImportMap(importer *PackageInfo) *ImportMap { + var ( + self string + collector *Collector + ) + if importer != nil { + self = importer.Path + collector = importer.collector + } + + return &ImportMap{ + Self: self, + + External: make(map[string]ImportInfo), + + counters: make(map[string]int), + collector: collector, + } +} + +// Merge merges the given import map into the receiver. +// The importing package must be the same. +func (imports *ImportMap) Merge(other *ImportMap) { + if other.Self != imports.Self { + panic("cannot merge import maps with different importing package") + } + + if other.ImportModels { + imports.ImportModels = true + } + + for path, info := range other.External { + if _, ok := imports.External[path]; ok { + continue + } + + counter := imports.counters[info.Name] + imports.counters[info.Name] = counter + 1 + + imports.External[path] = ImportInfo{ + Name: info.Name, + Index: counter, + RelPath: info.RelPath, + } + } +} + +// Add adds the given package to the import map if not already present, +// choosing import names so as to avoid collisions. +// +// Add does not support unsynchronised concurrent calls +// on the same receiver. +func (imports *ImportMap) Add(pkg *PackageInfo) { + if pkg.Path == imports.Self { + // Do not import self. + return + } + + if imports.External[pkg.Path].Name != "" { + // Package already imported. + return + } + + // Fetch and update counter for name. + counter := imports.counters[pkg.Name] + imports.counters[pkg.Name] = counter + 1 + + // Always add counters to + imports.External[pkg.Path] = ImportInfo{ + Name: pkg.Name, + Index: counter, + RelPath: computeImportPath(imports.Self, pkg.Path), + } +} + +// AddType adds all dependencies of the given type to the import map +// and marks all referenced named types as models. +// +// It is a runtime error to call AddType on an ImportMap +// created with nil importing package. +// +// AddType does not support unsynchronised concurrent calls +// on the same receiver. +func (imports *ImportMap) AddType(typ types.Type) { + imports.addTypeImpl(typ, new(typeutil.Map)) +} + +// addTypeImpl provides the actual implementation of AddType. +// The visited parameter is used to break cycles. +func (imports *ImportMap) addTypeImpl(typ types.Type, visited *typeutil.Map) { + collector := imports.collector + if collector == nil { + panic("AddType called on ImportMap with nil collector") + } + + for { // Avoid recursion where possible. + switch t := typ.(type) { + case *types.Alias, *types.Named: + if visited.Set(typ, true) != nil { + // Break type cycles. + return + } + + obj := typ.(interface{ Obj() *types.TypeName }).Obj() + if obj.Pkg() == nil { + // Ignore universe type. + return + } + + if obj.Pkg().Path() == imports.Self { + imports.ImportModels = true + } + + // Record model. + imports.collector.Model(obj) + + // Import parent package. + imports.Add(collector.Package(obj.Pkg())) + + instance, _ := typ.(interface{ TypeArgs() *types.TypeList }) + if instance != nil { + // Record type argument dependencies. + if targs := instance.TypeArgs(); targs != nil { + for i := range targs.Len() { + imports.addTypeImpl(targs.At(i), visited) + } + } + } + + if collector.options.UseInterfaces { + // No creation/initialisation code required. + return + } + + if _, isAlias := typ.(*types.Alias); isAlias { + // Aliased type might be needed during + // JS value creation and initialisation. + typ = types.Unalias(typ) + break + } + + if IsClass(typ) || IsAny(typ) || IsStringAlias(typ) { + return + } + + // If named type does not map to a class, unknown type or string, + // its underlying type may be needed during JS value creation. + typ = typ.Underlying() + + case *types.Basic: + switch { + case t.Info()&(types.IsBoolean|types.IsInteger|types.IsUnsigned|types.IsFloat|types.IsString) != 0: + break + case t.Info()&types.IsComplex != 0: + collector.logger.Warningf("package %s: complex types are not supported by encoding/json", imports.Self) + default: + collector.logger.Warningf("package %s: unknown basic type %s: please report this to Wails maintainers", imports.Self, typ) + } + return + + case *types.Array, *types.Pointer, *types.Slice: + typ = typ.(interface{ Elem() types.Type }).Elem() + + case *types.Chan: + collector.logger.Warningf("package %s: channel types are not supported by encoding/json", imports.Self) + return + + case *types.Map: + if IsMapKey(t.Key()) { + if IsStringAlias(t.Key()) { + // This model type is always rendered as a string alias, + // hence we can generate it and use it as a type for JS object keys. + imports.addTypeImpl(t.Key(), visited) + } + } else if IsTypeParam(t.Key()) { + // In some cases, type params or pointers to type params + // may be valid as map keys, but not for all instantiations. + // When that happens, emit a softer warning. + collector.logger.Warningf( + "package %s: type %s is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors", + imports.Self, types.TypeString(t.Key(), nil), + ) + } else { + collector.logger.Warningf( + "package %s: type %s is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors", + imports.Self, types.TypeString(t.Key(), nil), + ) + } + + typ = t.Elem() + + case *types.Signature: + collector.logger.Warningf("package %s: function types are not supported by encoding/json", imports.Self) + return + + case *types.Struct: + if t.NumFields() == 0 || MaybeJSONMarshaler(typ) != NonMarshaler || MaybeTextMarshaler(typ) != NonMarshaler { + // Struct is empty, or marshals to custom JSON (any) or string. + return + } + + // Retrieve struct info and ensure it is complete. + info := collector.Struct(t).Collect() + + if len(info.Fields) == 0 { + // No visible fields. + return + } + + // Add field dependencies. + for i := range len(info.Fields) - 1 { + imports.addTypeImpl(info.Fields[i].Type, visited) + } + + // Process last field without recursion. + typ = info.Fields[len(info.Fields)-1].Type + + case *types.Interface, *types.TypeParam: + // No dependencies. + return + + default: + collector.logger.Warningf("package %s: unknown type %s: please report this to Wails maintainers", imports.Self, typ) + return + } + } +} + +// computeImportPath returns the shortest relative import path +// through which the importer package can reference the imported one. +func computeImportPath(importer string, imported string) string { + rel, err := filepath.Rel(importer, imported) + if err != nil { + panic(err) + } + + rel = filepath.ToSlash(rel) + if rel[0] == '.' { + return rel + } else { + return "./" + rel + } +} diff --git a/v3/internal/generator/collect/index.go b/v3/internal/generator/collect/index.go new file mode 100644 index 000000000..ae4d31240 --- /dev/null +++ b/v3/internal/generator/collect/index.go @@ -0,0 +1,121 @@ +package collect + +import ( + "slices" + "strings" +) + +// PackageIndex lists all services, models and unexported models +// to be generated from a single package. +// +// When obtained through a call to [PackageInfo.Index], +// each service and model appears at most once; +// services and models are sorted +// by internal property (all exported first), then by name. +type PackageIndex struct { + Package *PackageInfo + + Services []*ServiceInfo + HasExportedServices bool // If true, there is at least one exported service. + + Models []*ModelInfo + HasExportedModels bool // If true, there is at least one exported model. +} + +// Index computes a [PackageIndex] for the selected language from the list +// of generated services and models and regenerates cached stats. +// +// Services and models appear at most once in the returned slices; +// services are sorted by name; +// exported models precede all unexported ones +// and both ranges are sorted by name. +// +// Index calls info.Collect, and therefore provides the same guarantees. +// It is safe for concurrent use. +func (info *PackageInfo) Index(TS bool) (index *PackageIndex) { + // Init index. + index = &PackageIndex{ + Package: info.Collect(), + } + + // Init stats + stats := &Stats{ + NumPackages: 1, + } + + // Gather services. + for _, value := range info.services.Range { + service := value.(*ServiceInfo) + if !service.IsEmpty() { + index.Services = append(index.Services, service) + // Mark presence of exported services + if !service.Internal { + index.HasExportedServices = true + } + // Update service stats. + stats.NumServices++ + stats.NumMethods += len(service.Methods) + } + } + + // Sort services by internal property (exported first), then by name. + slices.SortFunc(index.Services, func(s1 *ServiceInfo, s2 *ServiceInfo) int { + if s1 == s2 { + return 0 + } + + if s1.Internal != s2.Internal { + if s1.Internal { + return 1 + } else { + return -1 + } + } + + return strings.Compare(s1.Name, s2.Name) + }) + + // Gather models. + for _, value := range info.models.Range { + model := value.(*ModelInfo) + index.Models = append(index.Models, model) + // Mark presence of exported models + if !model.Internal { + index.HasExportedModels = true + } + // Update model stats. + if len(model.Values) > 0 { + stats.NumEnums++ + } else { + stats.NumModels++ + } + } + + // Sort models by internal property (exported first), then by name. + slices.SortFunc(index.Models, func(m1 *ModelInfo, m2 *ModelInfo) int { + if m1 == m2 { + return 0 + } + + if m1.Internal != m2.Internal { + if m1.Internal { + return 1 + } else { + return -1 + } + } + + return strings.Compare(m1.Name, m2.Name) + }) + + // Cache stats + info.stats.Store(stats) + + return +} + +// IsEmpty returns true if the given index +// contains no data for the selected language. +func (index *PackageIndex) IsEmpty() bool { + return !index.HasExportedServices && !index.HasExportedModels && len(index.Package.Injections) == 0 +} diff --git a/v3/internal/generator/collect/method.go b/v3/internal/generator/collect/method.go new file mode 100644 index 000000000..adff7aaaa --- /dev/null +++ b/v3/internal/generator/collect/method.go @@ -0,0 +1,115 @@ +package collect + +import ( + "go/ast" + "go/types" + "sync" +) + +// MethodInfo records information about a method declaration. +// +// Read accesses to any public field are only safe +// if a call to [MethodInfo.Collect] has completed before the access, +// for example by calling it in the accessing goroutine +// or before spawning the accessing goroutine. +type MethodInfo struct { + Name string + + // Abstract is true when the described method belongs to an interface. + Abstract bool + + Doc *ast.CommentGroup + Decl *GroupInfo + + obj *types.Func + node ast.Node + + collector *Collector + once sync.Once +} + +func newMethodInfo(collector *Collector, obj *types.Func) *MethodInfo { + return &MethodInfo{ + obj: obj, + collector: collector, + } +} + +// Method returns the unique MethodInfo instance +// associated to the given object within a collector. +// +// Method is safe for concurrent use. +func (collector *Collector) Method(obj *types.Func) *MethodInfo { + return collector.fromCache(obj).(*MethodInfo) +} + +func (info *MethodInfo) Object() types.Object { + return info.obj +} + +func (info *MethodInfo) Type() types.Type { + return info.obj.Type() +} + +func (info *MethodInfo) Node() ast.Node { + return info.Collect().node +} + +// Collect gathers information about the method described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *MethodInfo) Collect() *MethodInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + collector := info.collector + + info.Name = info.obj.Name() + + path := collector.findDeclaration(info.obj) + if path == nil { + recv := "" + if info.obj.Type() != nil { + recv = info.obj.Type().(*types.Signature).Recv().Type().String() + "." + } + + collector.logger.Warningf( + "package %s: method %s%s: could not find declaration for method object", + info.obj.Pkg().Path(), + recv, + info.obj.Name(), + ) + + // Provide dummy group. + info.Decl = newGroupInfo(nil).Collect() + return + } + + // path shape: *ast.FuncDecl/*ast.Field, ... + info.node = path[0] + + // Retrieve doc comments. + switch n := info.node.(type) { + case *ast.FuncDecl: + // Concrete method. + info.Doc = n.Doc + info.Decl = newGroupInfo(nil).Collect() // Provide dummy group. + + case *ast.Field: + // Abstract method. + info.Abstract = true + info.Decl = newGroupInfo(path[0]).Collect() + } + }) + + return info +} diff --git a/v3/internal/generator/collect/model.go b/v3/internal/generator/collect/model.go new file mode 100644 index 000000000..4e5b32cb5 --- /dev/null +++ b/v3/internal/generator/collect/model.go @@ -0,0 +1,381 @@ +package collect + +import ( + "cmp" + "go/ast" + "go/constant" + "go/types" + "slices" + "strings" + "sync" +) + +type ( + // ModelInfo records all information that is required + // to render JS/TS code for a model type. + // + // Read accesses to exported fields are only safe + // if a call to [ModelInfo.Collect] has completed before the access, + // for example by calling it in the accessing goroutine + // or before spawning the accessing goroutine. + ModelInfo struct { + *TypeInfo + + // Internal records whether the model + // should be exported by the index file. + Internal bool + + // Imports records dependencies for this model. + Imports *ImportMap + + // Type records the target type for an alias or derived model, + // the underlying type for an enum. + Type types.Type + + // Fields records the property list for a class or struct alias model, + // in order of declaration and grouped by their declaring [ast.Field]. + Fields [][]*ModelFieldInfo + + // Values records the value list for an enum model, + // in order of declaration and grouped + // by their declaring [ast.GenDecl] and [ast.ValueSpec]. + Values [][][]*ConstInfo + + // TypeParams records type parameter names for generic models. + TypeParams []string + + // Predicates caches the value of all type predicates for this model. + // + // WARN: whenever working with a generic uninstantiated model type, + // use these instead of invoking predicate functions, + // which may incur a large performance penalty. + Predicates Predicates + + collector *Collector + once sync.Once + } + + // ModelFieldInfo holds extended information + // about a struct field in a model type. + ModelFieldInfo struct { + *StructField + *FieldInfo + } + + // Predicates caches the value of all type predicates. + Predicates struct { + IsJSONMarshaler MarshalerKind + MaybeJSONMarshaler MarshalerKind + IsTextMarshaler MarshalerKind + MaybeTextMarshaler MarshalerKind + IsMapKey bool + IsTypeParam bool + IsStringAlias bool + IsClass bool + IsAny bool + } +) + +func newModelInfo(collector *Collector, obj *types.TypeName) *ModelInfo { + return &ModelInfo{ + TypeInfo: collector.Type(obj), + collector: collector, + } +} + +// Model retrieves the the unique [ModelInfo] instance +// associated to the given type object within a Collector. +// If none is present, Model initialises a new one +// registers it for code generation +// and schedules background collection activity. +// +// Model is safe for concurrent use. +func (collector *Collector) Model(obj *types.TypeName) *ModelInfo { + pkg := collector.Package(obj.Pkg()) + if pkg == nil { + return nil + } + + model, present := pkg.recordModel(obj) + if !present { + collector.scheduler.Schedule(func() { model.Collect() }) + } + + return model +} + +// Collect gathers information for the model described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *ModelInfo) Collect() *ModelInfo { + if info == nil { + return nil + } + + // Changes in the following logic must be reflected adequately + // by the predicates in properties.go, by ImportMap.AddType + // and by all render.Module methods. + + info.once.Do(func() { + collector := info.collector + obj := info.Object().(*types.TypeName) + + typ := obj.Type() + + // Collect type information. + info.TypeInfo.Collect() + + // Initialise import map. + info.Imports = NewImportMap(collector.Package(obj.Pkg())) + + // Setup fallback type. + info.Type = types.Universe.Lookup("any").Type() + + // Record whether the model should be exported. + info.Internal = !obj.Exported() + + // Parse directives. + for _, doc := range []*ast.CommentGroup{info.Doc, info.Decl.Doc} { + if doc == nil { + continue + } + for _, comment := range doc.List { + if IsDirective(comment.Text, "internal") { + info.Internal = true + } + } + } + + // Record type parameter names. + var isGeneric bool + if generic, ok := typ.(interface{ TypeParams() *types.TypeParamList }); ok { + tparams := generic.TypeParams() + isGeneric = tparams != nil + + if isGeneric && tparams.Len() > 0 { + info.TypeParams = make([]string, tparams.Len()) + for i := range tparams.Len() { + info.TypeParams[i] = tparams.At(i).Obj().Name() + } + } + } + + // Precompute predicates. + // Preinstantiate typ to avoid repeated instantiations in predicate code. + ityp := instantiate(typ) + info.Predicates = Predicates{ + IsJSONMarshaler: IsJSONMarshaler(ityp), + MaybeJSONMarshaler: MaybeJSONMarshaler(ityp), + IsTextMarshaler: IsTextMarshaler(ityp), + MaybeTextMarshaler: MaybeTextMarshaler(ityp), + IsMapKey: IsMapKey(ityp), + IsTypeParam: IsTypeParam(ityp), + IsStringAlias: IsStringAlias(ityp), + IsClass: IsClass(ityp), + IsAny: IsAny(ityp), + } + + var def types.Type + var constants []*types.Const + + switch t := typ.(type) { + case *types.Alias: + // Model is an alias: take rhs as definition. + // It is important not to skip alias chains with [types.Unalias] + // because in doing so we could end up with a private type from another package. + def = t.Rhs() + + // Test for constants with alias type, + // but only when non-generic alias resolves to a basic type + // (hence not to e.g. a named type). + if basic, ok := types.Unalias(def).(*types.Basic); ok { + if !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 { + // Non-generic alias resolves to a representable constant type: + // look for defined constants whose type is exactly the alias typ. + for _, name := range obj.Pkg().Scope().Names() { + if cnst, ok := obj.Pkg().Scope().Lookup(name).(*types.Const); ok { + alias, isAlias := cnst.Type().(*types.Alias) + if isAlias && cnst.Val().Kind() != constant.Unknown && alias.Obj() == t.Obj() { + constants = append(constants, cnst) + } + } + } + } + } + + case *types.Named: + // Model is a named type: + // jump directly to underlying type to match go semantics, + // i.e. do not render named types as aliases for other named types. + def = typ.Underlying() + + // Check whether it implements marshaler interfaces or has defined constants. + if info.Predicates.MaybeJSONMarshaler != NonMarshaler { + // Type marshals to a custom value of unknown shape. + // If it has explicit custom marshaling logic, render it as any; + // otherwise, delegate to the underlying type that must be the actual [json.Marshaler]. + if info.Predicates.MaybeJSONMarshaler == ExplicitMarshaler { + return + } + } else if info.Predicates.MaybeTextMarshaler != NonMarshaler { + // Type marshals to a custom string of unknown shape. + // If it has explicit custom marshaling logic, render it as string; + // otherwise, delegate to the underlying type that must be the actual [encoding.TextMarshaler]. + // + // One exception must be made for situations + // where the underlying type is a [json.Marshaler] but the model is not: + // in that case, we cannot delegate to the underlying type either. + // Note that in such a case the underlying type is never a pointer or interface, + // because those cannot have explicitly defined methods, + // hence it would not possible for the model not to be a [json.Marshaler] + // while the underlying type is. + if info.Predicates.MaybeTextMarshaler == ExplicitMarshaler || MaybeJSONMarshaler(def) != NonMarshaler { + info.Type = types.Typ[types.String] + return + } + } else if basic, ok := def.Underlying().(*types.Basic); ok { + // Test for enums (excluding marshalers and generic types). + if !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 { + // Named type is defined as a representable constant type: + // look for defined constants of that named type. + for _, name := range obj.Pkg().Scope().Names() { + if cnst, ok := obj.Pkg().Scope().Lookup(name).(*types.Const); ok { + if cnst.Val().Kind() != constant.Unknown && types.Identical(cnst.Type(), typ) { + constants = append(constants, cnst) + } + } + } + } + } + + default: + panic("model has unknown object kind (neither alias nor named type)") + } + + // Handle struct types. + strct, isStruct := def.(*types.Struct) + if isStruct && info.Predicates.MaybeJSONMarshaler == NonMarshaler && info.Predicates.MaybeTextMarshaler == NonMarshaler { + // Def is struct and model is not a marshaler: + // collect information about struct fields. + info.collectStruct(strct) + info.Type = nil + return + } + + // Record required imports. + info.Imports.AddType(def) + + // Handle enum types. + // constants slice is always empty for structs, marshalers. + if len(constants) > 0 { + // Collect information about enum values. + info.collectEnum(constants) + } + + // That's all, folks. Render as a TS alias. + info.Type = def + }) + + return info +} + +// collectEnum collects information about enum values and their declarations. +func (info *ModelInfo) collectEnum(constants []*types.Const) { + // Collect information about each constant object. + values := make([]*ConstInfo, len(constants)) + for i, cnst := range constants { + values[i] = info.collector.Const(cnst).Collect() + } + + // Sort values by grouping and source order. + slices.SortFunc(values, func(v1 *ConstInfo, v2 *ConstInfo) int { + // Skip comparisons for identical pointers. + if v1 == v2 { + return 0 + } + + // Sort first by source order of declaration group. + if v1.Decl != v2.Decl { + return cmp.Compare(v1.Decl.Pos, v2.Decl.Pos) + } + + // Then by source order of spec. + if v1.Spec != v2.Spec { + return cmp.Compare(v1.Spec.Pos, v2.Spec.Pos) + } + + // Then by source order of identifiers. + if v1.Pos != v2.Pos { + return cmp.Compare(v1.Pos, v2.Pos) + } + + // Finally by name (for constants whose source position is unknown). + return strings.Compare(v1.Name, v2.Name) + }) + + // Split value list into groups and subgroups. + var decl, spec *GroupInfo + decli, speci := -1, -1 + + for _, value := range values { + if value.Spec != spec { + spec = value.Spec + + if value.Decl == decl { + speci++ + } else { + decl = value.Decl + decli++ + speci = 0 + info.Values = append(info.Values, nil) + } + + info.Values[decli] = append(info.Values[decli], nil) + } + + info.Values[decli][speci] = append(info.Values[decli][speci], value) + } +} + +// collectStruct collects information about struct fields and their declarations. +func (info *ModelInfo) collectStruct(strct *types.Struct) { + collector := info.collector + + // Retrieve struct info. + structInfo := collector.Struct(strct).Collect() + + // Allocate result slice. + fields := make([]*ModelFieldInfo, len(structInfo.Fields)) + + // Collect fields. + for i, field := range structInfo.Fields { + // Record required imports. + info.Imports.AddType(field.Type) + + fields[i] = &ModelFieldInfo{ + StructField: field, + FieldInfo: collector.Field(field.Object).Collect(), + } + } + + // Split field list into groups, preserving the original order. + var decl *GroupInfo + decli := -1 + + for _, field := range fields { + if field.Decl != decl { + decl = field.Decl + decli++ + info.Fields = append(info.Fields, nil) + } + + info.Fields[decli] = append(info.Fields[decli], field) + } +} diff --git a/v3/internal/generator/collect/package.go b/v3/internal/generator/collect/package.go new file mode 100644 index 000000000..b03d5e577 --- /dev/null +++ b/v3/internal/generator/collect/package.go @@ -0,0 +1,293 @@ +package collect + +import ( + "cmp" + "go/ast" + "go/token" + "go/types" + "path/filepath" + "slices" + "strings" + "sync" + "sync/atomic" + + "golang.org/x/tools/go/packages" +) + +// PackageInfo records information about a package. +// +// Read accesses to fields Path, Name, Types, TypesInfo, Fset +// are safe at any time without any synchronisation. +// +// Read accesses to all other fields are only safe +// if a call to [PackageInfo.Collect] has completed before the access, +// for example by calling it in the accessing goroutine +// or before spawning the accessing goroutine. +// +// Concurrent write accesses are only allowed through the provided methods. +type PackageInfo struct { + // Path holds the canonical path of the described package. + Path string + + // Name holds the import name of the described package. + Name string + + // Types and TypesInfo hold type information for this package. + Types *types.Package + TypesInfo *types.Info + + // Fset holds the FileSet that was used to parse this package. + Fset *token.FileSet + + // Files holds parsed files for this package, + // ordered by start position to support binary search. + Files []*ast.File + + // Docs holds package doc comments. + Docs []*ast.CommentGroup + + // Includes holds a list of additional files to include + // with the generated bindings. + // It maps file names to their paths on disk. + Includes map[string]string + + // Injections holds a list of code lines to be injected + // into the package index file. + Injections []string + + // services records service types that have to be generated for this package. + // We rely upon [sync.Map] for atomic swapping support. + // Keys are *types.TypeName, values are *ServiceInfo. + services sync.Map + + // models records model types that have to be generated for this package. + // We rely upon [sync.Map] for atomic swapping support. + // Keys are *types.TypeName, values are *ModelInfo. + models sync.Map + + // stats caches statistics about this package. + stats atomic.Pointer[Stats] + + collector *Collector + once sync.Once +} + +func newPackageInfo(pkg *packages.Package, collector *Collector) *PackageInfo { + return &PackageInfo{ + Path: pkg.PkgPath, + Name: pkg.Name, + + Types: pkg.Types, + TypesInfo: pkg.TypesInfo, + + Fset: pkg.Fset, + Files: pkg.Syntax, + + collector: collector, + } +} + +// Package retrieves the the unique [PackageInfo] instance, if any, +// associated to the given package object within a Collector. +// +// Package is safe for concurrent use. +func (collector *Collector) Package(pkg *types.Package) *PackageInfo { + return collector.pkgs[pkg] +} + +// Iterate calls yield sequentially for each [PackageInfo] instance +// registered with the collector. If yield returns false, +// Iterate stops the iteration. +// +// Iterate is safe for concurrent use. +func (collector *Collector) Iterate(yield func(pkg *PackageInfo) bool) { + for _, pkg := range collector.pkgs { + if !yield(pkg) { + return + } + } +} + +// Stats returns cached statistics for this package. +// If [PackageInfo.Index] has not been called yet, it returns nil. +// +// Stats is safe for unsynchronised concurrent calls. +func (info *PackageInfo) Stats() *Stats { + return info.stats.Load() +} + +// Collect gathers information about the package described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *PackageInfo) Collect() *PackageInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + collector := info.collector + + // Sort files by source position. + if !slices.IsSortedFunc(info.Files, compareAstFiles) { + info.Files = slices.Clone(info.Files) + slices.SortFunc(info.Files, compareAstFiles) + } + + // Collect docs and parse directives. + for _, file := range info.Files { + if file.Doc == nil { + continue + } + + info.Docs = append(info.Docs, file.Doc) + + // Retrieve file directory. + pos := info.Fset.Position(file.Pos()) + if !pos.IsValid() { + collector.logger.Errorf( + "package %s: found AST file with unknown path: `wails:include` directives from that file will be ignored", + info.Path, + ) + } + dir := filepath.Dir(pos.Filename) + + // Parse directives. + if info.Includes == nil { + info.Includes = make(map[string]string) + } + for _, comment := range file.Doc.List { + switch { + case IsDirective(comment.Text, "inject"): + // Check condition. + line, cond, err := ParseCondition(ParseDirective(comment.Text, "inject")) + if err != nil { + collector.logger.Errorf( + "%s: in `wails:inject` directive: %v", + info.Fset.Position(comment.Pos()), + err, + ) + continue + } + + if !cond.Satisfied(collector.options) { + continue + } + + // Record injected line. + info.Injections = append(info.Injections, line) + + case pos.IsValid() && IsDirective(comment.Text, "include"): + // Check condition. + pattern, cond, err := ParseCondition(ParseDirective(comment.Text, "include")) + if err != nil { + collector.logger.Errorf( + "%s: in `wails:include` directive: %v", + info.Fset.Position(comment.Pos()), + err, + ) + continue + } + + if !cond.Satisfied(collector.options) { + continue + } + + // Collect matching files. + paths, err := filepath.Glob(filepath.Join(dir, pattern)) + if err != nil { + collector.logger.Errorf( + "%s: invalid pattern '%s' in `wails:include` directive: %v", + info.Fset.Position(comment.Pos()), + pattern, + err, + ) + continue + } else if len(paths) == 0 { + collector.logger.Warningf( + "%s: pattern '%s' in `wails:include` directive matched no files", + info.Fset.Position(comment.Pos()), + pattern, + ) + continue + } + + // Announce and record matching files. + for _, path := range paths { + name := strings.ToLower(filepath.Base(path)) + if old, ok := info.Includes[name]; ok { + collector.logger.Errorf( + "%s: duplicate included file name '%s' in package %s; old path: '%s'; new path: '%s'", + info.Fset.Position(comment.Pos()), + name, + info.Path, + old, + path, + ) + continue + } + + collector.logger.Debugf( + "including file '%s' as '%s' in package %s", + path, + name, + info.Path, + ) + + info.Includes[name] = path + } + } + } + } + }) + + return info +} + +// recordService adds the given service type object +// to the set of bindings generated for this package. +// It returns the unique [ServiceInfo] instance associated +// with the given type object. +// +// It is an error to pass in here a type whose parent package +// is not the one described by the receiver. +// +// recordService is safe for unsynchronised concurrent calls. +func (info *PackageInfo) recordService(obj *types.TypeName) *ServiceInfo { + // Fetch current value, then add if not already present. + service, _ := info.services.Load(obj) + if service == nil { + service, _ = info.services.LoadOrStore(obj, newServiceInfo(info.collector, obj)) + } + return service.(*ServiceInfo) +} + +// recordModel adds the given model type object +// to the set of models generated for this package. +// It returns the unique [ModelInfo] instance associated +// with the given type object. The present result is true +// if the model was already registered. +// +// It is an error to pass in here a type whose parent package +// is not the one described by the receiver. +// +// recordModel is safe for unsynchronised concurrent calls. +func (info *PackageInfo) recordModel(obj *types.TypeName) (model *ModelInfo, present bool) { + // Fetch current value, then add if not already present. + imodel, present := info.models.Load(obj) + if imodel == nil { + imodel, present = info.models.LoadOrStore(obj, newModelInfo(info.collector, obj)) + } + return imodel.(*ModelInfo), present +} + +// compareAstFiles compares two AST files by starting position. +func compareAstFiles(f1 *ast.File, f2 *ast.File) int { + return cmp.Compare(f1.FileStart, f2.FileStart) +} diff --git a/v3/internal/generator/collect/predicates.go b/v3/internal/generator/collect/predicates.go new file mode 100644 index 000000000..bcb910186 --- /dev/null +++ b/v3/internal/generator/collect/predicates.go @@ -0,0 +1,478 @@ +package collect + +// This file gathers functions that test useful properties of model types. +// The rationale for the way things are handled here +// is given in the example file found at ./_reference/json_marshaler_behaviour.go + +import ( + "go/token" + "go/types" + "iter" + + "golang.org/x/exp/typeparams" +) + +// Cached interface types. +var ( + ifaceTextMarshaler = types.NewInterfaceType([]*types.Func{ + types.NewFunc(token.NoPos, nil, "MarshalText", + types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple( + types.NewParam(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())), + types.NewParam(token.NoPos, nil, "", types.Universe.Lookup("error").Type()), + ), false)), + }, nil).Complete() + + ifaceJSONMarshaler = types.NewInterfaceType([]*types.Func{ + types.NewFunc(token.NoPos, nil, "MarshalJSON", + types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple( + types.NewParam(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())), + types.NewParam(token.NoPos, nil, "", types.Universe.Lookup("error").Type()), + ), false)), + }, nil).Complete() +) + +// MarshalerKind values describe +// whether and how a type implements a marshaler interface. +// For any one of the two marshaler interfaces, a type is +// - a NonMarshaler if it does not implement it; +// - an ImplicitMarshaler if it inherits the implementation from its underlying type; +// - an ExplicitMarshaler if it defines the relevant method explicitly. +type MarshalerKind byte + +const ( + NonMarshaler MarshalerKind = iota + ImplicitMarshaler + ExplicitMarshaler +) + +// termlist returns an iterator over the normalised term list of the given type. +// If typ is invalid or has an empty type set, termlist returns the empty sequence. +// If typ has an empty term list +// then termlist returns a sequence with just one element: the type itself. +// +// TODO: replace with new term set API once Go 1.25 is out. +// See go.dev/issue/61013 +func termlist(typ types.Type) iter.Seq[*typeparams.Term] { + terms, err := typeparams.NormalTerms(types.Unalias(typ)) + return func(yield func(*typeparams.Term) bool) { + if err == nil && len(terms) == 0 { + yield(typeparams.NewTerm(false, typ)) + } else { + for _, term := range terms { + if !yield(term) { + break + } + } + } + } +} + +// instantiate instantiates typ if it is an uninstantiated generic type +// using its own type parameters as arguments in order to preserve genericity. +// +// If typ is not generic or already instantiated, it is returned as is. +// If typ is not an alias, then the returned type is not an alias either. +func instantiate(typ types.Type) types.Type { + if t, ok := typ.(interface { + TypeParams() *types.TypeParamList + TypeArgs() *types.TypeList + }); ok && t.TypeParams() != nil && t.TypeArgs() == nil { + args := make([]types.Type, t.TypeParams().Len()) + for i := range args { + args[i] = t.TypeParams().At(i) + } + + typ, _ = types.Instantiate(nil, typ, args, false) + } + + return typ +} + +// isMarshaler checks whether the given type +// implements one of the two marshaler interfaces, +// and whether it implements it explicitly, +// i.e. by defining the relevant method directly +// instead of inheriting it from the underlying type. +// +// If addressable is true, it checks both pointer and non-pointer receivers. +// +// The behaviour of isMarshaler is unspecified +// if marshaler is not one of [json.Marshaler] or [encoding.TextMarshaler]. +func isMarshaler(typ types.Type, marshaler *types.Interface, addressable bool, visited map[*types.TypeName]MarshalerKind) MarshalerKind { + // Follow alias chain and instantiate if necessary. + // + // types.Implements does not handle generics, + // hence when typ is generic it must be instantiated. + // + // Instantiation operations may incur a large performance penalty and are usually cached, + // but doing so here would entail some complex global state and a potential memory leak. + // Because typ should be generic only during model collection, + // it should be enough to cache the result of marshaler queries for models. + typ = instantiate(types.Unalias(typ)) + + // Invariant: at this point, typ is not an alias. + + if typ == types.Typ[types.Invalid] { + // Do not pass invalid types to [types.Implements]. + return NonMarshaler + } + + result := types.Implements(typ, marshaler) + + ptr, isPtr := typ.Underlying().(*types.Pointer) + + if !result && addressable && !isPtr { + result = types.Implements(types.NewPointer(typ), marshaler) + } + + named, isNamed := typ.(*types.Named) + + if result { + // Check whether marshaler method is implemented explicitly on a named type. + if isNamed { + method := marshaler.Method(0).Name() + for i := range named.NumMethods() { + if named.Method(i).Name() == method { + return ExplicitMarshaler + } + } + } + + return ImplicitMarshaler + } + + // Fast path: named types that fail the [types.Implements] test cannot be marshalers. + // + // WARN: currently typeparams cannot be used on the rhs of a named type declaration. + // If that changes in the future, + // this guard will become essential for correctness, + // not just a shortcut. + if isNamed { + return NonMarshaler + } + + // Unwrap at most one pointer and follow alias chain. + if isPtr { + typ = types.Unalias(ptr.Elem()) + } + + // Invariant: at this point, typ is not an alias. + + // Type parameters require special handling: + // iterate over their term list and treat them as marshalers + // if so are all their potential instantiations. + + tp, ok := typ.(*types.TypeParam) + if !ok { + return NonMarshaler + } + + // Init cycle detection/deduplication map. + if visited == nil { + visited = make(map[*types.TypeName]MarshalerKind) + } + + // Type params cannot be embedded in constraints directly, + // but they can be embedded as pointer terms. + // + // When we hit that kind of cycle, + // we can err towards it being a marshaler: + // such a constraint is meaningless anyways, + // as no type can be simultaneously a pointer to itself. + // + // Therefore, we iterate the type set + // only for unvisited pointers-to-typeparams, + // and return the current best guess + // for those we have already visited. + // + // WARN: there has been some talk + // of allowing type parameters as embedded fields/terms. + // That might make our lives miserable here. + // The spec must be monitored for changes in that regard. + if isPtr { + if kind, ok := visited[tp.Obj()]; ok { + return kind + } + } + + // Initialise kind to explicit marshaler, then decrease as needed. + kind := ExplicitMarshaler + + if isPtr { + // Pointers are never explicit marshalers. + kind = ImplicitMarshaler + // Mark pointer-to-typeparam as visited and init current best guess. + visited[tp.Obj()] = kind + } + + // Iterate term list. + for term := range termlist(tp) { + ttyp := types.Unalias(term.Type()) + + // Reject if tp has a tilde or invalid element in its term list + // or has a method-only constraint. + // + // Valid tilde terms + // can always be satisfied by named types that hide their methods + // hence fail in general to implement the required interface. + if term.Tilde() || ttyp == types.Typ[types.Invalid] || ttyp == tp { + kind = NonMarshaler + break + } + + // Propagate the presence of a wrapping pointer. + if isPtr { + ttyp = types.NewPointer(ttyp) + } + + kind = min(kind, isMarshaler(ttyp, marshaler, addressable && !isPtr, visited)) + if kind == NonMarshaler { + // We can stop here as we've reached the minimum [MarshalerKind]. + break + } + } + + // Store final response for pointer-to-typeparam. + if isPtr { + visited[tp.Obj()] = kind + } + + return kind +} + +// IsTextMarshaler queries whether and how the given type +// implements the [encoding.TextMarshaler] interface. +func IsTextMarshaler(typ types.Type) MarshalerKind { + return isMarshaler(typ, ifaceTextMarshaler, false, nil) +} + +// MaybeTextMarshaler queries whether and how the given type +// implements the [encoding.TextMarshaler] interface for at least one receiver form. +func MaybeTextMarshaler(typ types.Type) MarshalerKind { + return isMarshaler(typ, ifaceTextMarshaler, true, nil) +} + +// IsJSONMarshaler queries whether and how the given type +// implements the [json.Marshaler] interface. +func IsJSONMarshaler(typ types.Type) MarshalerKind { + return isMarshaler(typ, ifaceJSONMarshaler, false, nil) +} + +// MaybeJSONMarshaler queries whether and how the given type +// implements the [json.Marshaler] interface for at least one receiver form. +func MaybeJSONMarshaler(typ types.Type) MarshalerKind { + return isMarshaler(typ, ifaceJSONMarshaler, true, nil) +} + +// IsMapKey returns true if the given type +// is accepted as a map key by encoding/json. +func IsMapKey(typ types.Type) bool { + // Iterate over type set and return true if all elements are valid. + // + // We cannot simply delegate to [IsTextMarshaler] here + // because a union of some basic terms and some TextMarshalers + // might still be acceptable. + // + // NOTE: If typ is not a typeparam or constraint, termlist returns just typ itself. + // If typ has an empty type set, it's safe to return true + // because the map cannot be instantiated anyways. + for term := range termlist(typ) { + ttyp := types.Unalias(term.Type()) + + // Types whose underlying type is a signed/unsigned integer or a string + // are always acceptable, whether they are marshalers or not. + if basic, ok := ttyp.Underlying().(*types.Basic); ok { + if basic.Info()&(types.IsInteger|types.IsUnsigned|types.IsString) != 0 { + continue + } + } + + // Valid tilde terms + // can always be satisfied by named types that hide their methods + // hence fail in general to implement the required interface. + // For example one could have: + // + // type NotAKey struct{ encoding.TextMarshaler } + // func (NotAKey) MarshalText() int { ... } + // + // which satisfies ~struct{ encoding.TextMarshaler } + // but is not itself a TextMarshaler. + // + // It might still be the case that the constraint + // requires explicitly a marshaling method, + // hence we perform one last check on typ. + // + // For example, we reject interface{ ~struct{ ... } } + // but still accept interface{ ~struct{ ... }; MarshalText() ([]byte, error) } + // + // All other cases are only acceptable + // if the type implements [encoding.TextMarshaler] in non-addressable mode. + if term.Tilde() || IsTextMarshaler(ttyp) == NonMarshaler { + // When some term fails, test the input typ itself, + // but only if it has not been tested already. + // + // Note that when term.Tilde() is true + // then it is always the case that typ != term.Type(), + // because cyclic constraints are not allowed + // and naked type parameters cannot occur in type unions. + return typ != term.Type() && IsTextMarshaler(typ) != NonMarshaler + } + } + + return true +} + +// IsTypeParam returns true when the given type +// is either a TypeParam or a pointer to a TypeParam. +func IsTypeParam(typ types.Type) bool { + switch t := types.Unalias(typ).(type) { + case *types.TypeParam: + return true + case *types.Pointer: + _, ok := types.Unalias(t.Elem()).(*types.TypeParam) + return ok + default: + return false + } +} + +// IsStringAlias returns true when +// either typ will be rendered to JS/TS as an alias for the TS type `string`, +// or typ itself (not its underlying type) is a pointer +// whose element type satisfies the property described above. +// +// This predicate is only safe to use either with map keys, +// where pointers are treated in an ad-hoc way by [json.Marshal], +// or when typ IS ALREADY KNOWN to be either [types.Alias] or [types.Named]. +// +// Otherwise, the result might be incorrect: +// IsStringAlias MUST NOT be used to check +// whether an arbitrary instance of [types.Type] +// renders as a JS/TS string type. +// +// Notice that IsStringAlias returns false for all type parameters: +// detecting those that must be always instantiated as string aliases +// is technically possible, but very difficult. +func IsStringAlias(typ types.Type) bool { + // Unwrap at most one pointer. + // NOTE: do not unalias typ before testing: + // aliases whose underlying type is a pointer + // are never rendered as strings. + if ptr, ok := typ.(*types.Pointer); ok { + typ = ptr.Elem() + } + + switch typ.(type) { + case *types.Alias, *types.Named: + // Aliases and named types might be rendered as string aliases. + default: + // Not a model type, hence not an alias. + return false + } + + // Skip pointer and interface types: they are always nullable + // and cannot have any explicitly defined methods. + // This takes care of rejecting type params as well, + // since their underlying type is guaranteed to be an interface. + switch typ.Underlying().(type) { + case *types.Pointer, *types.Interface: + return false + } + + // Follow alias chain. + typ = types.Unalias(typ) + + // Aliases of the basic string type are rendered as strings. + if basic, ok := typ.(*types.Basic); ok { + return basic.Info()&types.IsString != 0 + } + + // json.Marshalers can only be rendered as any. + // TextMarshalers that aren't json.Marshalers render as strings. + if MaybeJSONMarshaler(typ) != NonMarshaler { + return false + } else if MaybeTextMarshaler(typ) != NonMarshaler { + return true + } + + // Named types whose underlying type is a string are rendered as strings. + basic, ok := typ.Underlying().(*types.Basic) + return ok && basic.Info()&types.IsString != 0 +} + +// IsClass returns true if the given type will be rendered +// as a JS/TS model class (or interface). +func IsClass(typ types.Type) bool { + // Follow alias chain. + typ = types.Unalias(typ) + + if _, isNamed := typ.(*types.Named); !isNamed { + // Unnamed types are never rendered as classes. + return false + } + + // Struct named types without custom marshaling are rendered as classes. + _, isStruct := typ.Underlying().(*types.Struct) + return isStruct && MaybeJSONMarshaler(typ) == NonMarshaler && MaybeTextMarshaler(typ) == NonMarshaler +} + +// IsAny returns true if the given type +// is guaranteed to render as the TS any type or equivalent. +// +// It might return false negatives for generic aliases, +// hence should only be used with instantiated types +// or in contexts where false negatives are acceptable. +func IsAny(typ types.Type) bool { + // Follow alias chain. + typ = types.Unalias(typ) + + if MaybeJSONMarshaler(typ) != NonMarshaler { + // If typ is either a named type, an interface, a pointer or a struct, + // it will be rendered as (possibly an alias for) the TS any type. + // + // If it is a type parameter that implements json.Marshal, + // every possible concrete instantiation will implement json.Marshal, + // hence will be rendered as the TS any type. + return true + } + + if MaybeTextMarshaler(typ) != NonMarshaler { + // If type is either a named type, an interface, a pointer or a struct, + // it will be rendered as (possibly an alias for) + // the (possibly nullable) TS string type. + // + // If typ is a type parameter, we know at this point + // that it does not necessarily implement json.Marshaler, + // hence it will be possible to instantiate it in a way + // that renders as the (possibly nullable) TS string type. + return false + } + + if ptr, ok := typ.Underlying().(*types.Pointer); ok { + // Pointers render as the union of their element type with null. + // This is equivalent to the TS any type + // if and only if so is the element type. + return IsAny(ptr.Elem()) + } + + // All types listed below have rich TS equivalents, + // hence won't be equivalent to the TS any type. + // + // WARN: it is important to keep these lists explicit and up to date + // instead of listing the unsupported types (which would be much easier). + // + // By doing so, IsAny will keep working correctly + // in case future updates to the Go spec introduce new type families, + // thus buying the maintainers some time to patch the binding generator. + + // Retrieve underlying type. + switch t := typ.Underlying().(type) { + case *types.Basic: + // Complex types are not supported. + return t.Info()&(types.IsBoolean|types.IsInteger|types.IsUnsigned|types.IsFloat|types.IsString) == 0 + case *types.Array, *types.Slice, *types.Map, *types.Struct, *types.TypeParam: + return false + } + + return true +} diff --git a/v3/internal/generator/collect/service.go b/v3/internal/generator/collect/service.go new file mode 100644 index 000000000..ba486cc37 --- /dev/null +++ b/v3/internal/generator/collect/service.go @@ -0,0 +1,332 @@ +package collect + +import ( + "fmt" + "go/ast" + "go/types" + "strconv" + "sync" + + "github.com/wailsapp/wails/v3/internal/hash" + "golang.org/x/tools/go/types/typeutil" +) + +type ( + // ServiceInfo records all information that is required + // to render JS/TS code for a service type. + // + // Read accesses to any public field are only safe + // if a call to [ServiceInfo.Collect] has completed before the access, + // for example by calling it in the accessing goroutine + // or before spawning the accessing goroutine. + ServiceInfo struct { + *TypeInfo + + // Internal records whether the service + // should be exported by the index file. + Internal bool + + Imports *ImportMap + Methods []*ServiceMethodInfo + + // HasInternalMethods records whether the service + // defines lifecycle or http server methods. + HasInternalMethods bool + + // Injections stores a list of JS code lines + // that should be injected into the generated file. + Injections []string + + collector *Collector + once sync.Once + } + + // ServiceMethodInfo records all information that is required + // to render JS/TS code for a service method. + ServiceMethodInfo struct { + *MethodInfo + FQN string + ID string + Internal bool + Params []*ParamInfo + Results []types.Type + } + + // ParamInfo records all information that is required + // to render JS/TS code for a service method parameter. + ParamInfo struct { + Name string + Type types.Type + Blank bool + Variadic bool + } +) + +func newServiceInfo(collector *Collector, obj *types.TypeName) *ServiceInfo { + return &ServiceInfo{ + TypeInfo: collector.Type(obj), + collector: collector, + } +} + +// Service returns the unique ServiceInfo instance +// associated to the given object within a collector +// and registers it for code generation. +// +// Service is safe for concurrent use. +func (collector *Collector) Service(obj *types.TypeName) *ServiceInfo { + pkg := collector.Package(obj.Pkg()) + if pkg == nil { + return nil + } + + return pkg.recordService(obj) +} + +// IsEmpty returns true if no methods or code injections +// are present for this service, for the selected language. +func (info *ServiceInfo) IsEmpty() bool { + // Ensure information has been collected. + info.Collect() + return len(info.Methods) == 0 && len(info.Injections) == 0 +} + +// Collect gathers information about the service described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *ServiceInfo) Collect() *ServiceInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + collector := info.collector + obj := info.Object().(*types.TypeName) + + // Collect type information. + info.TypeInfo.Collect() + + // Initialise import map. + info.Imports = NewImportMap(collector.Package(obj.Pkg())) + + // Compute intuitive method set (i.e. both pointer and non-pointer receiver). + // Do not use a method set cache because + // - it would hurt concurrency (requires mutual exclusion), + // - it is only useful when the same type is queried many times; + // this may only happen here if some embedded types appear frequently, + // which should be far from average. + mset := typeutil.IntuitiveMethodSet(obj.Type(), nil) + + // Collect method information. + info.Methods = make([]*ServiceMethodInfo, 0, len(mset)) + for _, sel := range mset { + switch { + case internalServiceMethods[sel.Obj().Name()]: + info.HasInternalMethods = true + continue + case !sel.Obj().Exported(): + // Ignore unexported and internal methods. + continue + } + + methodInfo := info.collectMethod(sel.Obj().(*types.Func)) + if methodInfo != nil { + info.Methods = append(info.Methods, methodInfo) + } + } + + // Record whether the service should be exported. + info.Internal = !obj.Exported() + + // Parse directives. + for _, doc := range []*ast.CommentGroup{info.Doc, info.Decl.Doc} { + if doc == nil { + continue + } + for _, comment := range doc.List { + switch { + case IsDirective(comment.Text, "internal"): + info.Internal = true + + case IsDirective(comment.Text, "inject"): + // Check condition. + line, cond, err := ParseCondition(ParseDirective(comment.Text, "inject")) + if err != nil { + collector.logger.Errorf( + "%s: in `wails:inject` directive: %v", + collector.Package(obj.Pkg()).Fset.Position(comment.Pos()), + err, + ) + continue + } + + if !cond.Satisfied(collector.options) { + continue + } + + // Record injected line. + info.Injections = append(info.Injections, line) + } + } + } + }) + + return info +} + +// internalServiceMethod is a set of methods +// that are handled specially by the binding engine +// and must not be exposed to the frontend. +var internalServiceMethods = map[string]bool{ + "ServiceName": true, + "ServiceStartup": true, + "ServiceShutdown": true, + "ServeHTTP": true, +} + +// typeError caches the type-checker type for the Go error interface. +var typeError = types.Universe.Lookup("error").Type() + +// typeAny caches the empty interface type. +var typeAny = types.Universe.Lookup("any").Type().Underlying() + +// collectMethod collects and returns information about a service method. +// It is intended to be called only by ServiceInfo.Collect. +func (info *ServiceInfo) collectMethod(method *types.Func) *ServiceMethodInfo { + collector := info.collector + obj := info.Object().(*types.TypeName) + + signature, _ := method.Type().(*types.Signature) + if signature == nil { + // Skip invalid interface method. + // TODO: is this actually necessary? + return nil + } + + // Compute fully qualified name. + path := obj.Pkg().Path() + if obj.Pkg().Name() == "main" { + // reflect.Method.PkgPath is always "main" for the main package. + // This should not cause collisions because + // other main packages are not importable. + path = "main" + } + + fqn := path + "." + obj.Name() + "." + method.Name() + id := hash.Fnv(fqn) + + methodInfo := &ServiceMethodInfo{ + MethodInfo: collector.Method(method).Collect(), + FQN: fqn, + ID: strconv.FormatUint(uint64(id), 10), + Params: make([]*ParamInfo, 0, signature.Params().Len()), + Results: make([]types.Type, 0, signature.Results().Len()), + } + + // Parse directives. + if methodInfo.Doc != nil { + var methodIdFound bool + + for _, comment := range methodInfo.Doc.List { + switch { + case IsDirective(comment.Text, "ignore"): + return nil + + case IsDirective(comment.Text, "internal"): + methodInfo.Internal = true + + case !methodIdFound && IsDirective(comment.Text, "id"): + idString := ParseDirective(comment.Text, "id") + idValue, err := strconv.ParseUint(idString, 10, 32) + + if err != nil { + collector.logger.Errorf( + "%s: invalid value '%s' in `wails:id` directive: expected a valid uint32 value", + collector.Package(method.Pkg()).Fset.Position(comment.Pos()), + idString, + ) + continue + } + + // Announce and record alias. + collector.logger.Infof( + "package %s: method %s.%s: default ID %s replaced by %d", + path, + obj.Name(), + method.Name(), + methodInfo.ID, + idValue, + ) + methodInfo.ID = strconv.FormatUint(idValue, 10) + methodIdFound = true + } + } + } + + // Collect parameters. + for i := range signature.Params().Len() { + param := signature.Params().At(i) + + if i == 0 { + // Skip first parameter if it has context type. + named, ok := types.Unalias(param.Type()).(*types.Named) + if ok && named.Obj().Pkg().Path() == collector.systemPaths.ContextPackage && named.Obj().Name() == "Context" { + continue + } + } + + if types.IsInterface(param.Type()) && !types.Identical(param.Type(), typeAny) { + paramName := param.Name() + if paramName == "" || paramName == "_" { + paramName = fmt.Sprintf("#%d", i+1) + } + + collector.logger.Warningf( + "%s: parameter %s has non-empty interface type %s: this is not supported by encoding/json and will likely result in runtime errors", + collector.Package(method.Pkg()).Fset.Position(param.Pos()), + paramName, + param.Type(), + ) + } + + // Record type dependencies. + info.Imports.AddType(param.Type()) + + // Record parameter. + methodInfo.Params = append(methodInfo.Params, &ParamInfo{ + Name: param.Name(), + Type: param.Type(), + Blank: param.Name() == "" || param.Name() == "_", + }) + } + + if signature.Variadic() { + methodInfo.Params[len(methodInfo.Params)-1].Type = methodInfo.Params[len(methodInfo.Params)-1].Type.(*types.Slice).Elem() + methodInfo.Params[len(methodInfo.Params)-1].Variadic = true + } + + // Collect results. + for i := range signature.Results().Len() { + result := signature.Results().At(i) + + if types.Identical(result.Type(), typeError) { + // Skip error results, they are thrown as exceptions + continue + } + + // Record type dependencies. + info.Imports.AddType(result.Type()) + + // Record result. + methodInfo.Results = append(methodInfo.Results, result.Type()) + } + + return methodInfo +} diff --git a/v3/internal/generator/collect/stats.go b/v3/internal/generator/collect/stats.go new file mode 100644 index 000000000..984c7e504 --- /dev/null +++ b/v3/internal/generator/collect/stats.go @@ -0,0 +1,34 @@ +package collect + +import "time" + +type Stats struct { + NumPackages int + NumServices int + NumMethods int + NumEnums int + NumModels int + StartTime time.Time + EndTime time.Time +} + +func (stats *Stats) Start() { + stats.StartTime = time.Now() + stats.EndTime = stats.StartTime +} + +func (stats *Stats) Stop() { + stats.EndTime = time.Now() +} + +func (stats *Stats) Elapsed() time.Duration { + return stats.EndTime.Sub(stats.StartTime) +} + +func (stats *Stats) Add(other *Stats) { + stats.NumPackages += other.NumPackages + stats.NumServices += other.NumServices + stats.NumMethods += other.NumMethods + stats.NumEnums += other.NumEnums + stats.NumModels += other.NumModels +} diff --git a/v3/internal/generator/collect/struct.go b/v3/internal/generator/collect/struct.go new file mode 100644 index 000000000..b9a1b99ca --- /dev/null +++ b/v3/internal/generator/collect/struct.go @@ -0,0 +1,352 @@ +package collect + +import ( + "cmp" + "go/ast" + "go/types" + "reflect" + "slices" + "strings" + "sync" + "unicode" +) + +type ( + // StructInfo records the flattened field list for a struct type, + // taking into account JSON tags. + // + // The field list is initially empty. It will be populated + // upon calling [StructInfo.Collect] for the first time. + // + // Read accesses to the field list are only safe + // if a call to [StructInfo.Collect] has been completed before the access, + // for example by calling it in the accessing goroutine + // or before spawning the accessing goroutine. + StructInfo struct { + Fields []*StructField + + typ *types.Struct + + collector *Collector + once sync.Once + } + + // FieldInfo represents a single field in a struct. + StructField struct { + JsonName string // Avoid collisions with [FieldInfo.Name]. + Type types.Type + Optional bool + Quoted bool + + // Object holds the described type-checker object. + Object *types.Var + } +) + +func newStructInfo(collector *Collector, typ *types.Struct) *StructInfo { + return &StructInfo{ + typ: typ, + collector: collector, + } +} + +// Struct retrieves the unique [StructInfo] instance +// associated to the given type within a Collector. +// If none is present, a new one is initialised. +// +// Struct is safe for concurrent use. +func (collector *Collector) Struct(typ *types.Struct) *StructInfo { + // Cache by type pointer, do not use a typeutil.Map: + // - for models, it may result in incorrect comments; + // - for anonymous structs, it would probably bring little benefit + // because the probability of repetitions is much lower. + + return collector.fromCache(typ).(*StructInfo) +} + +func (*StructInfo) Object() types.Object { + return nil +} + +func (info *StructInfo) Type() types.Type { + return info.typ +} + +func (*StructInfo) Node() ast.Node { + return nil +} + +// Collect gathers information for the structure described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// The field list of the receiver is populated +// by the same flattening algorithm employed by encoding/json. +// JSON struct tags are accounted for. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *StructInfo) Collect() *StructInfo { + if info == nil { + return nil + } + + type fieldData struct { + *StructField + + // Data for the encoding/json flattening algorithm. + nameFromTag bool + index []int + } + + info.once.Do(func() { + // Flattened list of fields with additional information. + fields := make([]fieldData, 0, info.typ.NumFields()) + + // Queued embedded types for current and next level. + current := make([]fieldData, 0, info.typ.NumFields()) + next := make([]fieldData, 1, max(1, info.typ.NumFields())) + + // Count of queued embedded types for current and next level. + count := make(map[*types.Struct]int) + nextCount := make(map[*types.Struct]int) + + // Set of visited types to avoid duplicating work. + visited := make(map[*types.Struct]bool) + + next[0] = fieldData{ + StructField: &StructField{ + Type: info.typ, + }, + } + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, count + clear(nextCount) + + for _, embedded := range current { + // Scan embedded type for fields to include. + estruct := embedded.Type.Underlying().(*types.Struct) + + // Skip previously visited structs + if visited[estruct] { + continue + } + visited[estruct] = true + + // WARNING: do not reuse cached info for embedded structs. + // It may lead to incorrect results for subtle reasons. + + for i := range estruct.NumFields() { + field := estruct.Field(i) + + // Retrieve type of field, following aliases conservatively + // and unwrapping exactly one pointer. + ftype := field.Type() + if ptr, ok := types.Unalias(ftype).(*types.Pointer); ok { + ftype = ptr.Elem() + } + + // Detect struct alias and keep it. + fstruct, _ := types.Unalias(ftype).(*types.Struct) + if fstruct == nil { + // Not a struct alias, follow alias chain. + ftype = types.Unalias(ftype) + fstruct, _ = ftype.Underlying().(*types.Struct) + } + + if field.Embedded() { + if !field.Exported() && fstruct == nil { + // Ignore embedded fields of unexported non-struct types. + continue + } + } else if !field.Exported() { + // Ignore unexported non-embedded fields. + continue + } + + // Retrieve and parse json tag. + tag := reflect.StructTag(estruct.Tag(i)).Get("json") + name, optional, quoted, visible := parseTag(tag) + if !visible { + // Ignored by encoding/json. + continue + } + + if !isValidFieldName(name) { + // Ignore alternative name if invalid. + name = "" + } + + index := make([]int, len(embedded.index)+1) + copy(index, embedded.index) + index[len(embedded.index)] = i + + if name != "" || !field.Embedded() || fstruct == nil { + // Tag name is non-empty, + // or field is not embedded, + // or field is not structure: + // add to field list. + + if !info.collector.options.UseInterfaces { + // In class mode, mark parametric fields as optional + // because there is no way to know their default JS value in advance. + if _, isTypeParam := types.Unalias(field.Type()).(*types.TypeParam); isTypeParam { + optional = true + } + } + + finfo := fieldData{ + StructField: &StructField{ + JsonName: name, + Type: field.Type(), + Optional: optional, + Quoted: quoted, + + Object: field, + }, + nameFromTag: name != "", + index: index, + } + + if name == "" { + finfo.JsonName = field.Name() + } + + fields = append(fields, finfo) + if count[estruct] > 1 { + // The struct we are scanning + // appears multiple times at the current level. + // This means that all its fields are ambiguous + // and must disappear. + // Duplicate them so that the field selection phase + // below will erase them. + fields = append(fields, finfo) + } + + continue + } + + // Queue embedded field for next level. + // If it has been queued already, do not duplicate it. + nextCount[fstruct]++ + if nextCount[fstruct] == 1 { + next = append(next, fieldData{ + StructField: &StructField{ + Type: ftype, + }, + index: index, + }) + } + } + } + } + + // Prepare for field selection phase. + slices.SortFunc(fields, func(f1 fieldData, f2 fieldData) int { + // Sort by name first. + if diff := strings.Compare(f1.JsonName, f2.JsonName); diff != 0 { + return diff + } + + // Break ties by depth of occurrence. + if diff := cmp.Compare(len(f1.index), len(f2.index)); diff != 0 { + return diff + } + + // Break ties by presence of json tag (prioritize presence). + if f1.nameFromTag != f2.nameFromTag { + if f1.nameFromTag { + return -1 + } else { + return 1 + } + } + + // Break ties by order of occurrence. + return slices.Compare(f1.index, f2.index) + }) + + fieldCount := 0 + + // Keep for each name the dominant field, drop those for which ties + // still exist (ignoring order of occurrence). + for i, j := 0, 1; j <= len(fields); j++ { + if j < len(fields) && fields[i].JsonName == fields[j].JsonName { + continue + } + + // If there is only one field with the current name, + // or there is a dominant one, keep it. + if i+1 == j || len(fields[i].index) != len(fields[i+1].index) || fields[i].nameFromTag != fields[i+1].nameFromTag { + fields[fieldCount] = fields[i] + fieldCount++ + } + + i = j + } + + fields = fields[:fieldCount] + + // Sort by order of occurrence. + slices.SortFunc(fields, func(f1 fieldData, f2 fieldData) int { + return slices.Compare(f1.index, f2.index) + }) + + // Copy selected fields to receiver. + info.Fields = make([]*StructField, len(fields)) + for i, field := range fields { + info.Fields[i] = field.StructField + } + + info.typ = nil + }) + + return info +} + +// parseTag parses a json field tag and extracts +// all options recognised by encoding/json. +func parseTag(tag string) (name string, optional bool, quoted bool, visible bool) { + if tag == "-" { + return "", false, false, false + } else { + visible = true + } + + parts := strings.Split(tag, ",") + + name = parts[0] + + for _, option := range parts[1:] { + switch option { + case "omitempty", "omitzero": + optional = true + case "string": + quoted = true + } + } + + return +} + +// isValidFieldName determines whether a field name is valid +// according to encoding/json. +func isValidFieldName(name string) bool { + if name == "" { + return false + } + + for _, c := range name { + if !strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c) && !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + + return true +} diff --git a/v3/internal/generator/collect/type.go b/v3/internal/generator/collect/type.go new file mode 100644 index 000000000..24e5adefc --- /dev/null +++ b/v3/internal/generator/collect/type.go @@ -0,0 +1,113 @@ +package collect + +import ( + "go/ast" + "go/types" + "slices" + "sync" +) + +// TypeInfo records information about a type declaration. +// +// Read accesses to any public field are only safe +// if a call to [TypeInfo.Collect] has completed before the access, +// for example by calling it in the accessing goroutine +// or before spawning the accessing goroutine. +type TypeInfo struct { + Name string + + // Alias is true for type aliases. + Alias bool + + Doc *ast.CommentGroup + Decl *GroupInfo + + obj *types.TypeName + node ast.Node + + collector *Collector + once sync.Once +} + +// newTypeInfo initialises a descriptor for the given named type object. +func newTypeInfo(collector *Collector, obj *types.TypeName) *TypeInfo { + return &TypeInfo{ + obj: obj, + collector: collector, + } +} + +// Type returns the unique TypeInfo instance +// associated to the given object within a collector. +// +// Type is safe for concurrent use. +func (collector *Collector) Type(obj *types.TypeName) *TypeInfo { + return collector.fromCache(obj).(*TypeInfo) +} + +func (info *TypeInfo) Object() types.Object { + return info.obj +} + +func (info *TypeInfo) Type() types.Type { + return info.obj.Type() +} + +func (info *TypeInfo) Node() ast.Node { + return info.Collect().node +} + +// Collect gathers information about the type described by its receiver. +// It can be called concurrently by multiple goroutines; +// the computation will be performed just once. +// +// Collect returns the receiver for chaining. +// It is safe to call Collect with nil receiver. +// +// After Collect returns, the calling goroutine and all goroutines +// it might spawn afterwards are free to access +// the receiver's fields indefinitely. +func (info *TypeInfo) Collect() *TypeInfo { + if info == nil { + return nil + } + + info.once.Do(func() { + collector := info.collector + + info.Name = info.obj.Name() + info.Alias = info.obj.IsAlias() + + path := collector.findDeclaration(info.obj) + if path == nil { + collector.logger.Warningf( + "package %s: type %s: could not find declaration for type object", + info.obj.Pkg().Path(), + info.Name, + ) + + // Provide dummy group. + info.Decl = newGroupInfo(nil).Collect() + return + } + + // path shape: *ast.TypeSpec, *ast.GenDecl, *ast.File + tspec := path[0].(*ast.TypeSpec) + + // Retrieve doc comments. + info.Doc = tspec.Doc + if info.Doc == nil { + info.Doc = tspec.Comment + } else if tspec.Comment != nil { + info.Doc = &ast.CommentGroup{ + List: slices.Concat(tspec.Doc.List, tspec.Comment.List), + } + } + + info.Decl = collector.fromCache(path[1]).(*GroupInfo).Collect() + + info.node = path[0] + }) + + return info +} diff --git a/v3/internal/generator/config/file.go b/v3/internal/generator/config/file.go new file mode 100644 index 000000000..1e3bc7477 --- /dev/null +++ b/v3/internal/generator/config/file.go @@ -0,0 +1,64 @@ +package config + +import ( + "io" + "os" + "path/filepath" +) + +// FileCreator abstracts away file and directory creation. +// We use this to implement tests cleanly. +// +// Paths are always relative to the output directory. +// +// A FileCreator must allow concurrent calls to Create transparently. +// Each [io.WriteCloser] instance returned by a call to Create +// will be used by one goroutine at a time; but distinct instances +// must support concurrent use by distinct goroutines. +type FileCreator interface { + Create(path string) (io.WriteCloser, error) +} + +// FileCreatorFunc is an adapter to allow +// the use of ordinary functions as file creators. +type FileCreatorFunc func(path string) (io.WriteCloser, error) + +// Create calls f(path). +func (f FileCreatorFunc) Create(path string) (io.WriteCloser, error) { + return f(path) +} + +// NullCreator is a dummy file creator implementation. +// Calls to Create never fail and return +// a writer that discards all incoming data. +var NullCreator FileCreator = FileCreatorFunc(func(path string) (io.WriteCloser, error) { + return nullWriteCloser{}, nil +}) + +// DirCreator returns a file creator that creates files +// relative to the given output directory. +// +// It joins the output directory and the file path, +// calls [os.MkdirAll] on the directory part of the result, +// then [os.Create] on the full file path. +func DirCreator(outputDir string) FileCreator { + return FileCreatorFunc(func(path string) (io.WriteCloser, error) { + path = filepath.Join(outputDir, path) + + if err := os.MkdirAll(filepath.Dir(path), 0o777); err != nil { + return nil, err + } + + return os.Create(path) + }) +} + +type nullWriteCloser struct{} + +func (nullWriteCloser) Write(data []byte) (int, error) { + return len(data), nil +} + +func (nullWriteCloser) Close() error { + return nil +} diff --git a/v3/internal/generator/config/log.go b/v3/internal/generator/config/log.go new file mode 100644 index 000000000..bbbd6c0db --- /dev/null +++ b/v3/internal/generator/config/log.go @@ -0,0 +1,100 @@ +package config + +import ( + "fmt" + + "github.com/pterm/pterm" +) + +// A Logger instance provides methods to format and report messages +// intended for the end user. +// +// All Logger methods may be called concurrently by its consumers. +type Logger interface { + // Errorf should process its arguments as if they were passed to fmt.Sprintf + // and report the resulting string to the user as an error message. + Errorf(format string, a ...any) + + // Warningf should process its arguments as if they were passed to fmt.Sprintf + // and report the resulting string to the user as a warning message. + Warningf(format string, a ...any) + + // Infof should process its arguments as if they were passed to fmt.Sprintf + // and report the resulting string to the user as an informational message. + Infof(format string, a ...any) + + // Debugf should process its arguments as if they were passed to fmt.Sprintf + // and report the resulting string to the user as a debug message. + Debugf(format string, a ...any) + + // Statusf should process its arguments as if they were passed to fmt.Sprintf + // and report the resulting string to the user as a status message. + Statusf(format string, a ...any) +} + +// NullLogger is a dummy Logger implementation +// that discards all incoming messages. +var NullLogger Logger = nullLogger{} + +type nullLogger struct{} + +func (nullLogger) Errorf(format string, a ...any) {} +func (nullLogger) Warningf(format string, a ...any) {} +func (nullLogger) Infof(format string, a ...any) {} +func (nullLogger) Debugf(format string, a ...any) {} +func (nullLogger) Statusf(format string, a ...any) {} + +// DefaultPtermLogger returns a Logger implementation that writes +// to the default pterm printers for each logging level. +// +// If spinner is not nil, it is used to log status updates. +// The spinner must have been started already. +func DefaultPtermLogger(spinner *pterm.SpinnerPrinter) Logger { + return &PtermLogger{ + &pterm.Error, + &pterm.Warning, + &pterm.Info, + &pterm.Debug, + spinner, + } +} + +// PtermLogger is a Logger implementation that writes to pterm printers. +// If any field is nil, PtermLogger discards all messages of that level. +type PtermLogger struct { + Error pterm.TextPrinter + Warning pterm.TextPrinter + Info pterm.TextPrinter + Debug pterm.TextPrinter + Spinner *pterm.SpinnerPrinter +} + +func (logger *PtermLogger) Errorf(format string, a ...any) { + if logger.Error != nil { + logger.Error.Printfln(format, a...) + } +} + +func (logger *PtermLogger) Warningf(format string, a ...any) { + if logger.Warning != nil { + logger.Warning.Printfln(format, a...) + } +} + +func (logger *PtermLogger) Infof(format string, a ...any) { + if logger.Info != nil { + logger.Info.Printfln(format, a...) + } +} + +func (logger *PtermLogger) Debugf(format string, a ...any) { + if logger.Debug != nil { + logger.Debug.Printfln(format, a...) + } +} + +func (logger *PtermLogger) Statusf(format string, a ...any) { + if logger.Spinner != nil { + logger.Spinner.UpdateText(fmt.Sprintf(format, a...)) + } +} diff --git a/v3/internal/generator/config/paths.go b/v3/internal/generator/config/paths.go new file mode 100644 index 000000000..84d18214c --- /dev/null +++ b/v3/internal/generator/config/paths.go @@ -0,0 +1,10 @@ +package config + +// WailsAppPkgPath is the official import path of Wails v3's application package. +const WailsAppPkgPath = "github.com/wailsapp/wails/v3/pkg/application" + +// SystemPaths holds resolved paths of required system packages. +type SystemPaths struct { + ContextPackage string + ApplicationPackage string +} diff --git a/v3/internal/generator/constants.go b/v3/internal/generator/constants.go new file mode 100644 index 000000000..3c0c85873 --- /dev/null +++ b/v3/internal/generator/constants.go @@ -0,0 +1,54 @@ +package generator + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "strings" +) + +func GenerateConstants(goData []byte) (string, error) { + + // Create a new token file set and parser + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, "", goData, parser.AllErrors) + if err != nil { + return "", err + } + + // Extract constant declarations and generate JavaScript constants + var jsConstants []string + for _, decl := range f.Decls { + if gd, ok := decl.(*ast.GenDecl); ok && gd.Tok == token.CONST { + for _, spec := range gd.Specs { + if vs, ok := spec.(*ast.ValueSpec); ok { + for i, name := range vs.Names { + value := vs.Values[i] + if value != nil { + jsConstants = append(jsConstants, fmt.Sprintf("export const %s = %s;", name.Name, jsValue(value))) + } + } + } + } + } + } + + // Join the JavaScript constants into a single string + jsCode := strings.Join(jsConstants, "\n") + + return jsCode, nil +} + +func jsValue(expr ast.Expr) string { + // Implement conversion from Go constant value to JavaScript value here. + // You can add more cases for different types if needed. + switch e := expr.(type) { + case *ast.BasicLit: + return e.Value + case *ast.Ident: + return e.Name + default: + return "" + } +} diff --git a/v3/internal/generator/constants_test.go b/v3/internal/generator/constants_test.go new file mode 100644 index 000000000..9e056dcac --- /dev/null +++ b/v3/internal/generator/constants_test.go @@ -0,0 +1,55 @@ +package generator + +import "testing" + +func TestGenerateConstants(t *testing.T) { + tests := []struct { + name string + goData []byte + want string + wantErr bool + }{ + { + name: "int", + goData: []byte(`package test +const one = 1`), + want: "export const one = 1;", + wantErr: false, + }, + { + name: "float", + goData: []byte(`package test +const one_point_five = 1.5`), + want: "export const one_point_five = 1.5;", + wantErr: false, + }, + { + name: "string", + goData: []byte(`package test +const one_as_a_string = "1"`), + want: `export const one_as_a_string = "1";`, + wantErr: false, + }, + { + name: "nested", + goData: []byte(`package test +const ( + one_as_a_string = "1" +)`), + want: `export const one_as_a_string = "1";`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GenerateConstants(tt.goData) + if (err != nil) != tt.wantErr { + t.Errorf("GenerateConstants() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GenerateConstants() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v3/internal/generator/errors.go b/v3/internal/generator/errors.go new file mode 100644 index 000000000..cb000d1ad --- /dev/null +++ b/v3/internal/generator/errors.go @@ -0,0 +1,191 @@ +package generator + +import ( + "errors" + "fmt" + "maps" + "slices" + "sync" + + "github.com/wailsapp/wails/v3/internal/generator/config" +) + +// ErrNoContextPackage indicates that +// the canonical path for the standard context package +// did not match any actual package. +var ErrNoContextPackage = errors.New("standard context package not found at canonical import path ('context'): is the Wails v3 module properly installed? ") + +// ErrNoApplicationPackage indicates that +// the canonical path for the Wails application package +// did not match any actual package. +var ErrNoApplicationPackage = errors.New("Wails application package not found at canonical import path ('" + config.WailsAppPkgPath + "'): is the Wails v3 module properly installed? ") + +// ErrBadApplicationPackage indicates that +// the Wails application package has invalid content. +var ErrBadApplicationPackage = errors.New("package " + config.WailsAppPkgPath + ": function NewService has wrong signature: is the Wails v3 module properly installed? ") + +// ErrNoPackages is returned by [Generator.Generate] +// when [LoadPackages] returns no error and no packages. +var ErrNoPackages = errors.New("the given patterns matched no packages") + +// ErrorReport accumulates and logs error +// and warning messages, with deduplication. +// +// It implements the error interface; the Error method +// returns a report counting messages emitted so far. +// +// It also implements the interface [config.Logger] for convenience. +type ErrorReport struct { + logger config.Logger + + mu sync.Mutex + warnings map[string]bool + errors map[string]bool +} + +// NewErrorReport report initialises an ErrorReport instance +// with the provided Logger implementation. +// +// If logger is nil, messages will be accumulated but not logged. +func NewErrorReport(logger config.Logger) *ErrorReport { + if logger == nil { + logger = config.NullLogger + } + + return &ErrorReport{ + logger: logger, + warnings: make(map[string]bool), + errors: make(map[string]bool), + } +} + +// Error returns a string reporting the number +// of errors and warnings emitted so far. +func (report *ErrorReport) Error() string { + report.mu.Lock() + defer report.mu.Unlock() + + if len(report.errors) > 0 && len(report.warnings) == 0 { + var plural string + if len(report.errors) > 1 { + plural = "s" + } + return fmt.Sprintf("%d error%s emitted", len(report.errors), plural) + + } else if len(report.errors) == 0 && len(report.warnings) > 0 { + var plural string + if len(report.warnings) > 1 { + plural = "s" + } + + return fmt.Sprintf("%d warning%s emitted", len(report.warnings), plural) + + } else if len(report.errors) > 0 && len(report.warnings) > 0 { + var eplural, wplural string + if len(report.errors) > 1 { + eplural = "s" + } + if len(report.warnings) > 1 { + wplural = "s" + } + + return fmt.Sprintf("%d error%s and %d warning%s emitted", len(report.errors), eplural, len(report.warnings), wplural) + + } else { + return "no errors or warnings emitted" + } +} + +// HasErrors returns true if at least one error has been added to the report. +func (report *ErrorReport) HasErrors() bool { + report.mu.Lock() + result := len(report.errors) > 0 + report.mu.Unlock() + return result +} + +// HasWarnings returns true if at least one warning has been added to the report. +func (report *ErrorReport) HasWarnings() bool { + report.mu.Lock() + result := len(report.warnings) > 0 + report.mu.Unlock() + return result +} + +// Errors returns the list of error messages +// that have been added to the report. +// The order is randomised. +func (report *ErrorReport) Errors() []string { + report.mu.Lock() + defer report.mu.Unlock() + + return slices.Collect(maps.Keys(report.errors)) +} + +// Warnings returns the list of warning messages +// that have been added to the report. +// The order is randomised. +func (report *ErrorReport) Warnings() []string { + report.mu.Lock() + defer report.mu.Unlock() + + return slices.Collect(maps.Keys(report.warnings)) +} + +// Errorf formats an error message and adds it to the report. +// If not already present, the message is forwarded +// to the logger instance provided during initialisation. +func (report *ErrorReport) Errorf(format string, a ...any) { + msg := fmt.Sprintf(format, a...) + + report.mu.Lock() + defer report.mu.Unlock() + + present := report.errors[msg] + report.errors[msg] = true + + if !present { + report.logger.Errorf(format, a...) + } +} + +// Warningf formats an error message and adds it to the report. +// If not already present, the message is forwarded +// to the logger instance provided during initialisation. +func (report *ErrorReport) Warningf(format string, a ...any) { + msg := fmt.Sprintf(format, a...) + + report.mu.Lock() + defer report.mu.Unlock() + + present := report.warnings[msg] + report.warnings[msg] = true + + if !present { + report.logger.Warningf(format, a...) + } +} + +// Infof forwards the given informational message +// to the logger instance provided during initialisation. +// +// This method is here just for convenience and performs no deduplication. +func (report *ErrorReport) Infof(format string, a ...any) { + report.logger.Infof(format, a...) +} + +// Debugf forwards the given informational message +// to the logger instance provided during initialisation. +// +// This method is here just for convenience and performs no deduplication. +func (report *ErrorReport) Debugf(format string, a ...any) { + report.logger.Debugf(format, a...) +} + +// Statusf forwards the given status message +// to the logger instance provided during initialisation. +// +// This method is here just for convenience and performs no deduplication. +func (report *ErrorReport) Statusf(format string, a ...any) { + report.logger.Statusf(format, a...) +} diff --git a/v3/internal/generator/generate.go b/v3/internal/generator/generate.go new file mode 100644 index 000000000..a3162ea8a --- /dev/null +++ b/v3/internal/generator/generate.go @@ -0,0 +1,262 @@ +package generator + +import ( + "fmt" + "io" + "strings" + "sync" + "time" + + "github.com/wailsapp/wails/v3/internal/flags" + "github.com/wailsapp/wails/v3/internal/generator/collect" + "github.com/wailsapp/wails/v3/internal/generator/config" + "github.com/wailsapp/wails/v3/internal/generator/render" +) + +// Generator wraps all bookkeeping data structures that are needed +// to generate bindings for a set of packages. +type Generator struct { + options *flags.GenerateBindingsOptions + creator config.FileCreator + + // serviceFiles maps service file paths to their type object. + // It is used for lower/upper-case collision detection. + // Keys are strings, values are *types.TypeName. + serviceFiles sync.Map + + collector *collect.Collector + renderer *render.Renderer + + logger *ErrorReport + scheduler scheduler +} + +// NewGenerator configures a new generator instance. +// The options argument must not be nil. +// If creator is nil, no output file will be created. +// If logger is not nil, it is used to report messages interactively. +func NewGenerator(options *flags.GenerateBindingsOptions, creator config.FileCreator, logger config.Logger) *Generator { + if creator == nil { + creator = config.NullCreator + } + + report := NewErrorReport(logger) + + return &Generator{ + options: options, + creator: config.FileCreatorFunc(func(path string) (io.WriteCloser, error) { + report.Debugf("writing output file %s", path) + return creator.Create(path) + }), + + logger: report, + } +} + +// Generate runs the binding generation process +// for the packages specified by the given patterns. +// +// Concurrent or repeated calls to Generate with the same receiver +// are not allowed. +// +// The stats result field is never nil. +// +// The error result field is nil in case of complete success (no warning). +// Otherwise, it may either report errors that occured while loading +// the initial set of packages, or errors returned by the static analyser, +// or be an [ErrorReport] instance. +// +// If error is an ErrorReport, it may have accumulated no errors, just warnings. +// When this is the case, all bindings have been generated successfully. +// +// Parsing/type-checking errors or errors encountered while writing +// individual files will be printed directly to the [config.Logger] instance +// provided during initialisation. +func (generator *Generator) Generate(patterns ...string) (stats *collect.Stats, err error) { + stats = &collect.Stats{} + stats.Start() + defer stats.Stop() + + // Validate file names. + err = generator.validateFileNames() + if err != nil { + return + } + + // Parse build flags. + buildFlags, err := generator.options.BuildFlags() + if err != nil { + return + } + + // Start package loading feedback. + var lpkgMutex sync.Mutex + generator.logger.Statusf("Loading packages...") + go func() { + time.Sleep(5 * time.Second) + if lpkgMutex.TryLock() { + generator.logger.Statusf("Loading packages... (this may take a long time)") + lpkgMutex.Unlock() + } + }() + + systemPaths, err := ResolveSystemPaths(buildFlags) + if err != nil { + return + } + + // Load initial packages. + pkgs, err := LoadPackages(buildFlags, patterns...) + + // Suppress package loading feedback. + lpkgMutex.Lock() + + // Check for loading errors. + if err != nil { + return + } + if len(patterns) > 0 && len(pkgs) == 0 { + err = ErrNoPackages + return + } + + // Report parsing/type-checking errors. + for _, pkg := range pkgs { + for _, err := range pkg.Errors { + generator.logger.Warningf("%v", err) + } + } + + // Panic on repeated calls. + if generator.collector != nil { + panic("Generate() must not be called more than once on the same receiver") + } + + // Initialise subcomponents. + generator.collector = collect.NewCollector(pkgs, systemPaths, generator.options, &generator.scheduler, generator.logger) + generator.renderer = render.NewRenderer(generator.options, generator.collector) + + // Update status. + generator.logger.Statusf("Looking for services...") + serviceFound := sync.OnceFunc(func() { generator.logger.Statusf("Generating service bindings...") }) + + // Run static analysis. + services, err := FindServices(pkgs, systemPaths, generator.logger) + + // Check for analyser errors. + if err != nil { + return + } + + // Discard unneeded data. + pkgs = nil + + // Schedule code generation for each found service. + for obj := range services { + serviceFound() + generator.scheduler.Schedule(func() { + generator.generateService(obj) + }) + } + + // Wait until all services have been generated and all models collected. + generator.scheduler.Wait() + + // Invariants: + // - Service files have been generated for all discovered services; + // - ModelInfo.Collect has been called on all discovered models, and therefore + // - all required models have been discovered. + + // Update status. + if generator.options.NoIndex { + generator.logger.Statusf("Generating models...") + } else { + generator.logger.Statusf("Generating models and index files...") + } + + // Schedule models, index and included files generation for each package. + for info := range generator.collector.Iterate { + generator.scheduler.Schedule(func() { + generator.generateModelsIndexIncludes(info) + }) + } + + // Wait until all models and indices have been generated. + generator.scheduler.Wait() + + // Populate stats. + generator.logger.Statusf("Collecting stats...") + for info := range generator.collector.Iterate { + stats.Add(info.Stats()) + } + + // Return non-empty error report. + if generator.logger.HasErrors() || generator.logger.HasWarnings() { + err = generator.logger + } + + return +} + +// generateModelsIndexIncludes schedules generation of public/private model files, +// included files and, if allowed by the options, +// of an index file for the given package. +func (generator *Generator) generateModelsIndexIncludes(info *collect.PackageInfo) { + index := info.Index(generator.options.TS) + + // info.Index implies info.Collect: goroutines spawned below + // can access package information freely. + + if len(index.Models) > 0 { + generator.scheduler.Schedule(func() { + generator.generateModels(info, index.Models) + }) + } + + if len(index.Package.Includes) > 0 { + generator.scheduler.Schedule(func() { + generator.generateIncludes(index) + }) + } + + if !generator.options.NoIndex && !index.IsEmpty() { + generator.generateIndex(index) + } +} + +// validateFileNames validates user-provided filenames. +func (generator *Generator) validateFileNames() error { + switch { + case generator.options.ModelsFilename == "": + return fmt.Errorf("models filename must not be empty") + + case !generator.options.NoIndex && generator.options.IndexFilename == "": + return fmt.Errorf("package index filename must not be empty") + + case generator.options.ModelsFilename != strings.ToLower(generator.options.ModelsFilename): + return fmt.Errorf("models filename must not contain uppercase characters") + + case generator.options.IndexFilename != strings.ToLower(generator.options.IndexFilename): + return fmt.Errorf("package index filename must not contain uppercase characters") + + case !generator.options.NoIndex && generator.options.ModelsFilename == generator.options.IndexFilename: + return fmt.Errorf("models and package indexes cannot share the same filename") + } + + return nil +} + +// scheduler provides an implementation of the [collect.Scheduler] interface. +type scheduler struct { + sync.WaitGroup +} + +// Schedule runs the given function concurrently, +// registering it on the scheduler's wait group. +func (sched *scheduler) Schedule(task func()) { + sched.Add(1) + go func() { + defer sched.Done() + task() + }() +} diff --git a/v3/internal/generator/generate_test.go b/v3/internal/generator/generate_test.go new file mode 100644 index 000000000..0e05cb6cb --- /dev/null +++ b/v3/internal/generator/generate_test.go @@ -0,0 +1,241 @@ +package generator + +import ( + "errors" + "fmt" + "github.com/wailsapp/wails/v3/internal/generator/render" + "io" + "io/fs" + "os" + "path/filepath" + "slices" + "strings" + "sync" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/pterm/pterm" + "github.com/wailsapp/wails/v3/internal/flags" + "github.com/wailsapp/wails/v3/internal/generator/config" +) + +const testcases = "github.com/wailsapp/wails/v3/internal/generator/testcases/..." + +type testParams struct { + name string + options *flags.GenerateBindingsOptions + outputDir string + want map[string]bool +} + +func TestGenerator(t *testing.T) { + const ( + useNamesBit = 1 << iota + useInterfacesBit + tsBit + ) + + // Generate configuration matrix. + tests := make([]*testParams, 1<<3) + for i := range tests { + options := &flags.GenerateBindingsOptions{ + ModelsFilename: "models", + IndexFilename: "index", + + UseBundledRuntime: true, + + TS: i&tsBit != 0, + UseInterfaces: i&useInterfacesBit != 0, + UseNames: i&useNamesBit != 0, + } + + name := configString(options) + + tests[i] = &testParams{ + name: name, + options: options, + outputDir: filepath.Join("testdata/output", name), + want: make(map[string]bool), + } + } + + for _, test := range tests { + // Create output dir. + if err := os.MkdirAll(test.outputDir, 0777); err != nil { + t.Fatal(err) + } + + // Walk output dir. + err := filepath.WalkDir(test.outputDir, func(path string, d fs.DirEntry, err error) error { + // Skip directories. + if d.IsDir() { + return nil + } + + // Skip got files. + if strings.HasSuffix(d.Name(), ".got.js") || strings.HasSuffix(d.Name(), ".got.ts") { + return nil + } + + // Record file. + test.want[filepath.Clean("."+path[len(test.outputDir):])] = false + return nil + }) + + if err != nil { + t.Fatal(err) + } + } + + // Run tests. + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + creator := outputCreator(t, test) + + generator := NewGenerator( + test.options, + creator, + config.DefaultPtermLogger(nil), + ) + + _, err := generator.Generate(testcases) + if report := (*ErrorReport)(nil); errors.As(err, &report) { + if report.HasErrors() { + t.Error(report) + } else if report.HasWarnings() { + pterm.Warning.Println(report) + } + + // Log warnings and compare with reference output. + if log, err := creator.Create("warnings.log"); err != nil { + t.Error(err) + } else { + func() { + defer log.Close() + + warnings := report.Warnings() + slices.Sort(warnings) + + for _, msg := range warnings { + fmt.Fprint(log, msg, render.Newline) + } + }() + } + } else if err != nil { + t.Error(err) + } + + for path, present := range test.want { + if !present { + t.Errorf("Missing output file '%s'", path) + } + } + }) + } +} + +// configString computes a subtest name from the given configuration. +func configString(options *flags.GenerateBindingsOptions) string { + lang := "JS" + if options.TS { + lang = "TS" + } + return fmt.Sprintf("lang=%s/UseInterfaces=%v/UseNames=%v", lang, options.UseInterfaces, options.UseNames) +} + +// outputCreator returns a FileCreator that detects want/got pairs +// and schedules them for comparison. +// +// If no corresponding want file exists, it is created and reported. +func outputCreator(t *testing.T, params *testParams) config.FileCreator { + var mu sync.Mutex + return config.FileCreatorFunc(func(path string) (io.WriteCloser, error) { + path = filepath.Clean(path) + prefixedPath := filepath.Join(params.outputDir, path) + + // Protect want map accesses. + mu.Lock() + defer mu.Unlock() + + if seen, ok := params.want[path]; ok { + // File exists: mark as seen and compare. + if seen { + t.Errorf("Duplicate output file '%s'", path) + } + params.want[path] = true + + // Open want file. + wf, err := os.Open(prefixedPath) + if err != nil { + return nil, err + } + + // Create or truncate got file. + ext := filepath.Ext(prefixedPath) + gf, err := os.Create(fmt.Sprintf("%s.got%s", prefixedPath[:len(prefixedPath)-len(ext)], ext)) + if err != nil { + return nil, err + } + + // Initialise comparer. + return &outputComparer{t, path, wf, gf}, nil + } else { + // File does not exist: create it. + t.Errorf("Unexpected output file '%s'", path) + params.want[path] = true + + if err := os.MkdirAll(filepath.Dir(prefixedPath), 0777); err != nil { + return nil, err + } + + return os.Create(prefixedPath) + } + }) +} + +// outputComparer is a io.WriteCloser that writes to got. +// +// When Close is called, it compares want to got; if they are identical, +// it deletes got; otherwise it reports a testing error. +type outputComparer struct { + t *testing.T + path string + want *os.File + got *os.File +} + +func (comparer *outputComparer) Write(data []byte) (int, error) { + return comparer.got.Write(data) +} + +func (comparer *outputComparer) Close() error { + defer comparer.want.Close() + defer comparer.got.Close() + + comparer.got.Seek(0, io.SeekStart) + + // Read want data. + want, err := io.ReadAll(comparer.want) + if err != nil { + comparer.t.Error(err) + return nil + } + + got, err := io.ReadAll(comparer.got) + if err != nil { + comparer.t.Error(err) + return nil + } + + if diff := cmp.Diff(want, got); diff != "" { + comparer.t.Errorf("Output file '%s' mismatch (-want +got):\n%s", comparer.path, diff) + } else { + // On success, delete got file. + comparer.got.Close() + if err := os.Remove(comparer.got.Name()); err != nil { + comparer.t.Error(err) + } + } + + return nil +} diff --git a/v3/internal/generator/includes.go b/v3/internal/generator/includes.go new file mode 100644 index 000000000..b8bed2392 --- /dev/null +++ b/v3/internal/generator/includes.go @@ -0,0 +1,100 @@ +package generator + +import ( + "io" + "os" + "path/filepath" + "slices" + "strings" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// generateIncludes copies included files to the package directory +// for the package summarised by the given index. +func (generator *Generator) generateIncludes(index *collect.PackageIndex) { + for name, path := range index.Package.Includes { + // Validate filename. + switch name { + case generator.renderer.ModelsFile(): + if index.HasExportedModels { + generator.logger.Errorf( + "package %s: included file '%s' collides with models filename; please rename the file or choose a different filename for models", + index.Package.Path, + path, + ) + return + } + + case generator.renderer.IndexFile(): + if !generator.options.NoIndex && !index.IsEmpty() { + generator.logger.Errorf( + "package %s: included file '%s' collides with JS/TS index filename; please rename the file or choose a different filename for JS/TS indexes", + index.Package.Path, + path, + ) + return + } + } + + // Validate against services. + service, ok := slices.BinarySearchFunc(index.Services, name, func(service *collect.ServiceInfo, name string) int { + return strings.Compare(generator.renderer.ServiceFile(service.Name), name) + }) + if ok { + generator.logger.Errorf( + "package %s: included file '%s' collides with filename for service %s; please rename either the file or the service", + index.Package.Path, + path, + index.Services[service].Name, + ) + return + } + + // Copy file to destination in separate goroutine. + generator.scheduler.Schedule(func() { + src, err := os.Open(path) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: could not read included file '%s'", index.Package.Path, path) + return + } + defer src.Close() + + stat, err := src.Stat() + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: could not read included file '%s'", index.Package.Path, path) + return + } + + if stat.IsDir() { + generator.logger.Errorf( + "package %s: included file '%s' is a directory; please glob or list all descendants explicitly", + index.Package.Path, + path, + ) + return + } + + dst, err := generator.creator.Create(filepath.Join(index.Package.Path, name)) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: could not write included file '%s'", index.Package.Path, name) + return + } + defer func() { + if err := dst.Close(); err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: could not write included file '%s'", index.Package.Path, name) + } + }() + + _, err = io.Copy(dst, src) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: could not copy included file '%s'", index.Package.Path, name) + } + }) + } +} diff --git a/v3/internal/generator/index.go b/v3/internal/generator/index.go new file mode 100644 index 000000000..e7a73c71f --- /dev/null +++ b/v3/internal/generator/index.go @@ -0,0 +1,53 @@ +package generator + +import ( + "path/filepath" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// generateIndex generates an index file from the given index information. +func (generator *Generator) generateIndex(index *collect.PackageIndex) { + defer generator.reportDualRoles(index) + + file, err := generator.creator.Create(filepath.Join(index.Package.Path, generator.renderer.IndexFile())) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: index generation failed", index.Package.Path) + return + } + defer func() { + if err := file.Close(); err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: index generation failed", index.Package.Path) + } + }() + + err = generator.renderer.Index(file, index) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: index generation failed", index.Package.Path) + } +} + +// reportDualRoles checks for models types that are also service types +// and emits a warning. +func (generator *Generator) reportDualRoles(index *collect.PackageIndex) { + services, models := index.Services, index.Models + for len(services) > 0 && len(models) > 0 { + if services[0].Name < models[0].Name { + services = services[1:] + } else if services[0].Name > models[0].Name { + models = models[1:] + } else { + generator.logger.Warningf( + "package %s: type %s has been marked both as a service and as a model; shadowing between the two may take place when importing generated JS indexes", + index.Package.Path, + services[0].Name, + ) + + services = services[1:] + models = models[1:] + } + } +} diff --git a/v3/internal/generator/load.go b/v3/internal/generator/load.go new file mode 100644 index 000000000..a0d6f5f6b --- /dev/null +++ b/v3/internal/generator/load.go @@ -0,0 +1,110 @@ +package generator + +import ( + "go/ast" + "go/parser" + "go/token" + + "github.com/wailsapp/wails/v3/internal/generator/config" + "golang.org/x/tools/go/packages" +) + +// ResolveSystemPaths resolves paths for the Wails system +func ResolveSystemPaths(buildFlags []string) (paths *config.SystemPaths, err error) { + // Resolve context pkg path. + contextPkgPaths, err := ResolvePatterns(buildFlags, "context") + if err != nil { + return + } else if len(contextPkgPaths) < 1 { + err = ErrNoContextPackage + return + } else if len(contextPkgPaths) > 1 { + // This should never happen... + panic("context package path matched multiple packages") + } + + // Resolve wails app pkg path. + wailsAppPkgPaths, err := ResolvePatterns(buildFlags, config.WailsAppPkgPath) + if err != nil { + return + } else if len(wailsAppPkgPaths) < 1 { + err = ErrNoApplicationPackage + return + } else if len(wailsAppPkgPaths) > 1 { + // This should never happen... + panic("wails application package path matched multiple packages") + } + + paths = &config.SystemPaths{ + ContextPackage: contextPkgPaths[0], + ApplicationPackage: wailsAppPkgPaths[0], + } + return +} + +// ResolvePatterns returns a slice containing all package paths +// that match the given patterns, according to the underlying build tool +// and within the context of the current working directory. +func ResolvePatterns(buildFlags []string, patterns ...string) (paths []string, err error) { + rewrittenPatterns := make([]string, len(patterns)) + for i, pattern := range patterns { + rewrittenPatterns[i] = "pattern=" + pattern + } + + pkgs, err := packages.Load(&packages.Config{ + Mode: packages.NeedName, + BuildFlags: buildFlags, + }, rewrittenPatterns...) + + for _, pkg := range pkgs { + paths = append(paths, pkg.PkgPath) + } + + return +} + +// LoadPackages loads the packages specified by the given patterns +// and their whole dependency tree. It returns a slice containing +// all packages that match the given patterns and all of their direct +// and indirect dependencies. +// +// The returned slice is in post-order w.r.t. the dependency relation, +// i.e. if package A depends on package B, then package B precedes package A. +// +// All returned package instances include syntax trees and full type information. +// +// Syntax is loaded in the context of a global [token.FileSet], +// which is available through the field [packages.Package.Fset] +// on each returned package. Therefore, source positions +// are canonical across all loaded packages. +func LoadPackages(buildFlags []string, patterns ...string) (pkgs []*packages.Package, err error) { + rewrittenPatterns := make([]string, len(patterns)) + for i, pattern := range patterns { + rewrittenPatterns[i] = "pattern=" + pattern + } + + // Global file set. + fset := token.NewFileSet() + + roots, err := packages.Load(&packages.Config{ + // NOTE: some Go maintainers now believe deprecation was an error and recommend using Load* modes + // (see e.g. https://github.com/golang/go/issues/48226#issuecomment-1948792315). + Mode: packages.LoadAllSyntax, + BuildFlags: buildFlags, + Fset: fset, + ParseFile: func(fset *token.FileSet, filename string, src []byte) (file *ast.File, err error) { + file, err = parser.ParseFile(fset, filename, src, parser.ParseComments|parser.SkipObjectResolution) + return + }, + }, rewrittenPatterns...) + + // Flatten dependency tree. + packages.Visit(roots, nil, func(pkg *packages.Package) { + if pkg.Fset != fset { + panic("fileset missing or not the global one") + } + pkgs = append(pkgs, pkg) + }) + + return +} diff --git a/v3/internal/generator/models.go b/v3/internal/generator/models.go new file mode 100644 index 000000000..2fe4f407f --- /dev/null +++ b/v3/internal/generator/models.go @@ -0,0 +1,39 @@ +package generator + +import ( + "path/filepath" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// generateModels generates a JS/TS models file for the given list of models. +// A call to info.Collect must complete before entering generateModels. +func (generator *Generator) generateModels(info *collect.PackageInfo, models []*collect.ModelInfo) { + // Merge all import maps. + imports := collect.NewImportMap(info) + for _, model := range models { + imports.Merge(model.Imports) + } + + // Clear irrelevant imports. + imports.ImportModels = false + + file, err := generator.creator.Create(filepath.Join(info.Path, generator.renderer.ModelsFile())) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: models generation failed", info.Path) + return + } + defer func() { + if err := file.Close(); err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: models generation failed", info.Path) + } + }() + + err = generator.renderer.Models(file, imports, models) + if err != nil { + generator.logger.Errorf("%v", err) + generator.logger.Errorf("package %s: models generation failed", info.Path) + } +} diff --git a/v3/internal/generator/render/create.go b/v3/internal/generator/render/create.go new file mode 100644 index 000000000..1332345d1 --- /dev/null +++ b/v3/internal/generator/render/create.go @@ -0,0 +1,391 @@ +package render + +import ( + "fmt" + "go/types" + "strings" + "text/template" + + "github.com/wailsapp/wails/v3/internal/generator/collect" + "golang.org/x/tools/go/types/typeutil" +) + +// SkipCreate returns true if the given array of types needs no creation code. +func (m *module) SkipCreate(ts []types.Type) bool { + for _, typ := range ts { + if m.NeedsCreate(typ) { + return false + } + } + return true +} + +// NeedsCreate returns true if the given type needs some creation code. +func (m *module) NeedsCreate(typ types.Type) bool { + return m.needsCreateImpl(typ, new(typeutil.Map)) +} + +// needsCreateImpl provides the actual implementation of NeedsCreate. +// The visited parameter is used to break cycles. +func (m *module) needsCreateImpl(typ types.Type, visited *typeutil.Map) bool { + switch t := typ.(type) { + case *types.Alias: + return m.needsCreateImpl(types.Unalias(typ), visited) + + case *types.Named: + if visited.Set(typ, true) != nil { + // The only way to hit a cycle here + // is through a chain of structs, nested pointers and arrays (not slices). + // We can safely return false at this point + // as the final answer is independent of the cycle. + return false + } + + if t.Obj().Pkg() == nil { + // Builtin named type: render underlying type. + return m.needsCreateImpl(t.Underlying(), visited) + } + + if collect.IsAny(typ) || collect.IsStringAlias(typ) { + break + } else if collect.IsClass(typ) { + return true + } else { + return m.needsCreateImpl(t.Underlying(), visited) + } + + case *types.Array, *types.Pointer: + return m.needsCreateImpl(typ.(interface{ Elem() types.Type }).Elem(), visited) + + case *types.Map, *types.Slice: + return true + + case *types.Struct: + if t.NumFields() == 0 || collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler || collect.MaybeTextMarshaler(typ) != collect.NonMarshaler { + return false + } + + info := m.collector.Struct(t) + info.Collect() + + for _, field := range info.Fields { + if m.needsCreateImpl(field.Type, visited) { + return true + } + } + + case *types.TypeParam: + return true + } + + return false +} + +// JSCreate renders JS/TS code that creates an instance +// of the given type from JSON data. +// +// JSCreate's output may be incorrect +// if m.Imports.AddType has not been called for the given type. +func (m *module) JSCreate(typ types.Type) string { + return m.JSCreateWithParams(typ, "") +} + +// JSCreateWithParams renders JS/TS code that creates an instance +// of the given type from JSON data. For generic types, +// it renders parameterised code. +// +// JSCreateWithParams's output may be incorrect +// if m.Imports.AddType has not been called for the given type. +func (m *module) JSCreateWithParams(typ types.Type, params string) string { + if len(params) > 0 && !m.hasTypeParams(typ) { + // Forget params for non-generic types. + params = "" + } + + switch t := typ.(type) { + case *types.Alias: + return m.JSCreateWithParams(types.Unalias(typ), params) + + case *types.Array, *types.Pointer: + pp, ok := m.postponedCreates.At(typ).(*postponed) + if ok { + return fmt.Sprintf("$$createType%d%s", pp.index, params) + } + + createElement := m.JSCreateWithParams(typ.(interface{ Elem() types.Type }).Elem(), params) + if createElement != "$Create.Any" { + pp = &postponed{m.postponedCreates.Len(), params} + m.postponedCreates.Set(typ, pp) + return fmt.Sprintf("$$createType%d%s", pp.index, params) + } + + case *types.Map: + pp, ok := m.postponedCreates.At(typ).(*postponed) + if !ok { + m.JSCreateWithParams(t.Elem(), params) + pp = &postponed{m.postponedCreates.Len(), params} + m.postponedCreates.Set(typ, pp) + } + + return fmt.Sprintf("$$createType%d%s", pp.index, params) + + case *types.Named: + if t.Obj().Pkg() == nil { + // Builtin named type: render underlying type. + return m.JSCreateWithParams(t.Underlying(), params) + } + + if !m.NeedsCreate(typ) { + break + } + + pp, ok := m.postponedCreates.At(typ).(*postponed) + if !ok { + if t.TypeArgs() != nil && t.TypeArgs().Len() > 0 { + // Postpone type args. + for i := range t.TypeArgs().Len() { + m.JSCreateWithParams(t.TypeArgs().At(i), params) + } + } + + pp = &postponed{m.postponedCreates.Len(), params} + m.postponedCreates.Set(typ, pp) + + if !collect.IsClass(typ) { + m.JSCreateWithParams(t.Underlying(), params) + } + } + + return fmt.Sprintf("$$createType%d%s", pp.index, params) + + case *types.Slice: + if types.Identical(typ, typeByteSlice) { + return "$Create.ByteSlice" + } + + pp, ok := m.postponedCreates.At(typ).(*postponed) + if !ok { + m.JSCreateWithParams(t.Elem(), params) + pp = &postponed{m.postponedCreates.Len(), params} + m.postponedCreates.Set(typ, pp) + } + + return fmt.Sprintf("$$createType%d%s", pp.index, params) + + case *types.Struct: + if t.NumFields() == 0 || collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler || collect.MaybeTextMarshaler(typ) != collect.NonMarshaler { + break + } + + pp, ok := m.postponedCreates.At(typ).(*postponed) + if ok { + return fmt.Sprintf("$$createType%d%s", pp.index, params) + } + + info := m.collector.Struct(t) + info.Collect() + + postpone := false + for _, field := range info.Fields { + if m.JSCreateWithParams(field.Type, params) != "$Create.Any" { + postpone = true + } + } + + if postpone { + pp = &postponed{m.postponedCreates.Len(), params} + m.postponedCreates.Set(typ, pp) + return fmt.Sprintf("$$createType%d%s", pp.index, params) + } + + case *types.TypeParam: + return fmt.Sprintf("$$createParam%s", typeparam(t.Index(), t.Obj().Name())) + } + + return "$Create.Any" +} + +// PostponedCreates returns the list of postponed create functions +// for the given module. +func (m *module) PostponedCreates() []string { + result := make([]string, m.postponedCreates.Len()) + + m.postponedCreates.Iterate(func(key types.Type, value any) { + pp := value.(*postponed) + + pre, post := "", "" + if pp.params != "" { + if m.TS { + pre = createParamRegex.ReplaceAllString(pp.params, "${0}: any") + " => " + } else { + pre = "/** @type {(...args: any[]) => any} */(" + pp.params + " => " + post = ")" + } + } + + switch t := key.(type) { + case *types.Array, *types.Slice: + result[pp.index] = fmt.Sprintf("%s$Create.Array(%s)%s", pre, m.JSCreateWithParams(t.(interface{ Elem() types.Type }).Elem(), pp.params), post) + + case *types.Map: + result[pp.index] = fmt.Sprintf("%s$Create.Map($Create.Any, %s)%s", pre, m.JSCreateWithParams(t.Elem(), pp.params), post) + + case *types.Named: + if !collect.IsClass(key) { + // Creation functions for non-struct named types + // require an indirect assignment to break cycles. + + // Typescript cannot infer the return type on its own: add hints. + cast, argType, returnType := "", "", "" + if m.TS { + argType = ": any[]" + returnType = ": any" + } else { + cast = "/** @type {(...args: any[]) => any} */" + } + + result[pp.index] = fmt.Sprintf(` +%s(function $$initCreateType%d(...args%s)%s { + if ($$createType%d === $$initCreateType%d) { + $$createType%d = %s%s%s; + } + return $$createType%d(...args); +})`, + cast, pp.index, argType, returnType, + pp.index, pp.index, + pp.index, pre, m.JSCreateWithParams(t.Underlying(), pp.params), post, + pp.index, + )[1:] // Remove initial newline. + + // We're done. + break + } + + var builder strings.Builder + + builder.WriteString(pre) + + if t.Obj().Pkg().Path() == m.Imports.Self { + if m.Imports.ImportModels { + builder.WriteString("$models.") + } + } else { + builder.WriteString(jsimport(m.Imports.External[t.Obj().Pkg().Path()])) + builder.WriteRune('.') + } + builder.WriteString(jsid(t.Obj().Name())) + builder.WriteString(".createFrom") + + if t.TypeArgs() != nil && t.TypeArgs().Len() > 0 { + builder.WriteString("(") + for i := range t.TypeArgs().Len() { + if i > 0 { + builder.WriteString(", ") + } + builder.WriteString(m.JSCreateWithParams(t.TypeArgs().At(i), pp.params)) + } + builder.WriteString(")") + } + builder.WriteString(post) + + result[pp.index] = builder.String() + + case *types.Pointer: + result[pp.index] = fmt.Sprintf("%s$Create.Nullable(%s)%s", pre, m.JSCreateWithParams(t.Elem(), pp.params), post) + + case *types.Struct: + info := m.collector.Struct(t) + info.Collect() + + var builder strings.Builder + builder.WriteString(pre) + builder.WriteString("$Create.Struct({") + + for _, field := range info.Fields { + createField := m.JSCreateWithParams(field.Type, pp.params) + if createField == "$Create.Any" { + continue + } + + builder.WriteString("\n \"") + template.JSEscape(&builder, []byte(field.JsonName)) + builder.WriteString("\": ") + builder.WriteString(createField) + builder.WriteRune(',') + } + + if len(info.Fields) > 0 { + builder.WriteRune('\n') + } + builder.WriteString("})") + builder.WriteString(post) + + result[pp.index] = builder.String() + + default: + result[pp.index] = pre + "$Create.Any" + post + } + }) + + if Newline != "\n" { + // Replace newlines according to local git config. + for i := range result { + result[i] = strings.ReplaceAll(result[i], "\n", Newline) + } + } + + return result +} + +type postponed struct { + index int + params string +} + +// hasTypeParams returns true if the given type depends upon type parameters. +func (m *module) hasTypeParams(typ types.Type) bool { + switch t := typ.(type) { + case *types.Alias: + if t.Obj().Pkg() == nil { + // Builtin alias: these are never rendered as templates. + return false + } + + return m.hasTypeParams(types.Unalias(typ)) + + case *types.Array, *types.Pointer, *types.Slice: + return m.hasTypeParams(typ.(interface{ Elem() types.Type }).Elem()) + + case *types.Map: + return m.hasTypeParams(t.Key()) || m.hasTypeParams(t.Elem()) + + case *types.Named: + if t.Obj().Pkg() == nil { + // Builtin named type: these are never rendered as templates. + return false + } + + if targs := t.TypeArgs(); targs != nil { + for i := range targs.Len() { + if m.hasTypeParams(targs.At(i)) { + return true + } + } + } + + case *types.Struct: + info := m.collector.Struct(t) + info.Collect() + + for _, field := range info.Fields { + if m.hasTypeParams(field.Type) { + return true + } + } + + case *types.TypeParam: + return true + } + + return false +} diff --git a/v3/internal/generator/render/default.go b/v3/internal/generator/render/default.go new file mode 100644 index 000000000..793c704ba --- /dev/null +++ b/v3/internal/generator/render/default.go @@ -0,0 +1,179 @@ +package render + +import ( + "fmt" + "go/types" + "strings" + "text/template" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// JSDefault renders the Javascript representation +// of the zero value of the given type, +// using the receiver's import map to resolve dependencies. +// +// JSDefault's output may be incorrect +// if imports.AddType has not been called for the given type. +func (m *module) JSDefault(typ types.Type, quoted bool) (result string) { + switch t := typ.(type) { + case *types.Alias, *types.Named: + result, ok := m.renderNamedDefault(t.(aliasOrNamed), quoted) + if ok { + return result + } + + case *types.Array: + if t.Len() == 0 { + return "[]" + } else { + // Initialise array with expected number of elements + return fmt.Sprintf("Array.from({ length: %d }, () => %s)", t.Len(), m.JSDefault(t.Elem(), false)) + } + + case *types.Slice: + if types.Identical(typ, typeByteSlice) { + return `""` + } else { + return "[]" + } + + case *types.Basic: + return m.renderBasicDefault(t, quoted) + + case *types.Map: + return "{}" + + case *types.Struct: + return m.renderStructDefault(t) + + case *types.TypeParam: + // Should be unreachable + panic("type parameters have no default value") + } + + // Fall back to null. + // encoding/json ignores null values so this is safe. + return "null" +} + +// renderBasicDefault outputs the Javascript representation +// of the zero value for the given basic type. +func (*module) renderBasicDefault(typ *types.Basic, quoted bool) string { + switch { + case typ.Info()&types.IsBoolean != 0: + if quoted { + return `"false"` + } else { + return "false" + } + + case typ.Info()&types.IsNumeric != 0 && typ.Info()&types.IsComplex == 0: + if quoted { + return `"0"` + } else { + return "0" + } + + case typ.Info()&types.IsString != 0: + if quoted { + return `'""'` + } else { + return `""` + } + } + + // Fall back to untyped mode. + if quoted { + return `""` + } else { + // encoding/json ignores null values so this is safe. + return "null" + } +} + +// renderNamedDefault outputs the Javascript representation +// of the zero value for the given alias or named type. +// The result field named 'ok' is true when the resulting code is valid. +// If false, it must be discarded. +func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result string, ok bool) { + if typ.Obj().Pkg() == nil { + // Builtin alias or named type: render underlying type. + return m.JSDefault(typ.Underlying(), quoted), true + } + + if quoted { + // WARN: Do not test with IsAny/IsStringAlias here!! We only want to catch marshalers. + if collect.MaybeJSONMarshaler(typ) == collect.NonMarshaler && collect.MaybeTextMarshaler(typ) == collect.NonMarshaler { + if basic, ok := typ.Underlying().(*types.Basic); ok { + // Quoted mode for basic alias/named type that is not a marshaler: delegate. + return m.renderBasicDefault(basic, quoted), true + } + // No need to handle typeparams: they are initialised to null anyways. + } + } + + prefix := "" + if m.Imports.ImportModels { + prefix = "$models." + } + + if collect.IsAny(typ) { + return "", false + } else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler { + return `""`, true + } else if collect.IsClass(typ) && !istpalias(typ) { + if typ.Obj().Pkg().Path() == m.Imports.Self { + return fmt.Sprintf("(new %s%s())", prefix, jsid(typ.Obj().Name())), true + } else { + return fmt.Sprintf("(new %s.%s())", jsimport(m.Imports.External[typ.Obj().Pkg().Path()]), jsid(typ.Obj().Name())), true + } + } else if _, isAlias := typ.(*types.Alias); isAlias { + return m.JSDefault(types.Unalias(typ), quoted), true + } else if len(m.collector.Model(typ.Obj()).Collect().Values) > 0 { + if typ.Obj().Pkg().Path() == m.Imports.Self { + return fmt.Sprintf("%s%s.$zero", prefix, jsid(typ.Obj().Name())), true + } else { + return fmt.Sprintf("%s.%s.$zero", jsimport(m.Imports.External[typ.Obj().Pkg().Path()]), jsid(typ.Obj().Name())), true + } + } else { + return m.JSDefault(typ.Underlying(), quoted), true + } +} + +// renderStructDefault outputs the Javascript representation +// of the zero value for the given struct type. +func (m *module) renderStructDefault(typ *types.Struct) string { + if collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler { + return "null" + } else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler { + return `""` + } + + info := m.collector.Struct(typ) + info.Collect() + + var builder strings.Builder + + builder.WriteRune('{') + for i, field := range info.Fields { + if field.Optional { + continue + } + + if i > 0 { + builder.WriteString(", ") + } + + builder.WriteRune('"') + template.JSEscape(&builder, []byte(field.JsonName)) + builder.WriteRune('"') + + builder.WriteString(": ") + + builder.WriteString(m.JSDefault(field.Type, field.Quoted)) + } + builder.WriteRune('}') + + return builder.String() +} diff --git a/v3/internal/generator/render/doc.go b/v3/internal/generator/render/doc.go new file mode 100644 index 000000000..8ed46d9c5 --- /dev/null +++ b/v3/internal/generator/render/doc.go @@ -0,0 +1,136 @@ +package render + +import ( + "bufio" + "bytes" + "go/ast" + "strings" + "unicode" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// hasdoc checks whether the given comment group contains actual doc comments. +func hasdoc(group *ast.CommentGroup) bool { + if group == nil { + return false + } + + // TODO: this is horrible, make it more efficient? + return strings.ContainsFunc(group.Text(), func(r rune) bool { return !unicode.IsSpace(r) }) +} + +var commentTerminator = []byte("*/") + +// jsdoc splits the given comment into lines and rewrites it as follows: +// - first, line terminators are stripped; +// - then a line terminator, the indent string and ' * ' +// are prepended to each line; +// - occurrences of the comment terminator '*/' are replaced with '* /' +// to avoid accidentally terminating the surrounding comment. +// +// All lines thus modified are joined back together. +// +// The returned string can be inserted in a multiline JSDoc comment +// with the given indentation. +func jsdoc(comment string, indent string) string { + var builder strings.Builder + prefix := []byte(Newline + indent + " * ") + + scanner := bufio.NewScanner(bytes.NewReader([]byte(comment))) + for scanner.Scan() { + line := scanner.Bytes() + + // Prepend prefix. + builder.Write(prefix) + + // Escape comment terminators. + for t := bytes.Index(line, commentTerminator); t >= 0; t = bytes.Index(line, commentTerminator) { + builder.Write(line[:t+1]) + builder.WriteRune(' ') + line = line[t+1:] + } + + builder.Write(line) + } + + return builder.String() +} + +// jsdocline removes all newlines in the given comment +// and escapes comment terminators using the same strategy as jsdoc. +func jsdocline(comment string) string { + var builder strings.Builder + + scanner := bufio.NewScanner(bytes.NewReader([]byte(comment))) + for scanner.Scan() { + line := bytes.TrimSpace(scanner.Bytes()) + if len(line) == 0 { + // Skip empty lines. + continue + } + + // Prepend space to separate lines. + builder.WriteRune(' ') + + // Escape comment terminators. + for t := bytes.Index(line, commentTerminator); t >= 0; t = bytes.Index(line, commentTerminator) { + builder.Write(line[:t+1]) + builder.WriteRune(' ') + line = line[t+1:] + } + + builder.Write(line) + } + + // Return resulting string, but skip initial space. + return builder.String()[1:] +} + +// isjsdocid returns true if the given string is a valid ECMAScript identifier, +// excluding unicode escape sequences. This is the property name format supported by JSDoc. +func isjsdocid(name string) bool { + for i, r := range name { + if i == 0 && !id_start(r) && r != '$' && r != '_' { + return false + } else if i > 0 && !id_continue(r) && r != '$' { + return false + } + } + return true +} + +// isjsdocobj returns true if all field names in the given model +// are valid jsdoc property names. +func isjsdocobj(model *collect.ModelInfo) bool { + if len(model.Fields) == 0 { + return false + } + for _, decl := range model.Fields { + for _, field := range decl { + if !isjsdocid(field.JsonName) { + return false + } + } + } + return true +} + +// id_start returns true if the given rune is in the ID_Start category +// according to UAX#31 (https://unicode.org/reports/tr31/). +func id_start(r rune) bool { + return (unicode.IsLetter(r) || + unicode.Is(unicode.Nl, r) || + unicode.Is(unicode.Other_ID_Start, r)) && !unicode.Is(unicode.Pattern_Syntax, r) && !unicode.Is(unicode.Pattern_White_Space, r) +} + +// id_continue returns true if the given rune is in the ID_Continue category +// according to UAX#31 (https://unicode.org/reports/tr31/). +func id_continue(r rune) bool { + return (id_start(r) || + unicode.Is(unicode.Mn, r) || + unicode.Is(unicode.Mc, r) || + unicode.Is(unicode.Nd, r) || + unicode.Is(unicode.Pc, r) || + unicode.Is(unicode.Other_ID_Continue, r)) && !unicode.Is(unicode.Pattern_Syntax, r) && !unicode.Is(unicode.Pattern_White_Space, r) +} diff --git a/v3/internal/generator/render/functions.go b/v3/internal/generator/render/functions.go new file mode 100644 index 000000000..6123095e8 --- /dev/null +++ b/v3/internal/generator/render/functions.go @@ -0,0 +1,107 @@ +package render + +import ( + "fmt" + "go/types" + "math/big" + "strconv" + "strings" + "text/template" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// tmplFunctions holds a map of utility functions +// that should be available in every template. +var tmplFunctions = template.FuncMap{ + "fixext": fixext, + "hasdoc": hasdoc, + "isjsdocid": isjsdocid, + "isjsdocobj": isjsdocobj, + "istpalias": istpalias, + "jsdoc": jsdoc, + "jsdocline": jsdocline, + "jsid": jsid, + "jsimport": jsimport, + "jsparam": jsparam, + "jsvalue": jsvalue, + "modelinfo": modelinfo, + "typeparam": typeparam, + "unalias": types.Unalias, +} + +// fixext replaces a *.ts extension with *.js in the given string. +// This is necessary to allow emitting javascript with the Typescript compiler. +func fixext(path string) string { + if strings.HasSuffix(path, ".ts") { + return path[:len(path)-3] + ".js" + } else { + return path + } +} + +// jsimport formats an external import name +// by joining the name with its occurrence index. +// Names are modified even when the index is 0 +// to avoid collisions with Go identifiers. +func jsimport(info collect.ImportInfo) string { + return fmt.Sprintf("%s$%d", info.Name, info.Index) +} + +// jsparam renders the JS name of a parameter. +// Blank parameters are replaced with a dollar sign followed by the given index. +// Non-blank parameters are escaped by [jsid]. +func jsparam(index int, param *collect.ParamInfo) string { + if param.Blank { + return "$" + strconv.Itoa(index) + } else { + return jsid(param.Name) + } +} + +// typeparam renders the TS name of a type parameter. +// Blank parameters are replaced with a double dollar sign +// followed by the given index. +// Non-blank parameters are escaped with jsid. +func typeparam(index int, param string) string { + if param == "" || param == "_" { + return "$$" + strconv.Itoa(index) + } else { + return jsid(param) + } +} + +// jsvalue renders a Go constant value to its Javascript representation. +func jsvalue(value any) string { + switch v := value.(type) { + case bool: + if v { + return "true" + } else { + return "false" + } + case string: + return fmt.Sprintf(`"%s"`, template.JSEscapeString(v)) + case int64: + return strconv.FormatInt(v, 10) + case *big.Int: + return v.String() + case *big.Float: + return v.Text('e', -1) + case *big.Rat: + return v.RatString() + } + + // Fall back to undefined. + return "(void(0))" +} + +// istpalias determines whether typ is an alias +// that when uninstantiated resolves to a typeparam. +func istpalias(typ types.Type) bool { + if alias, ok := typ.(*types.Alias); ok { + return collect.IsTypeParam(alias.Origin()) + } + + return false +} diff --git a/v3/internal/generator/render/identifier.go b/v3/internal/generator/render/identifier.go new file mode 100644 index 000000000..7a7b89f9d --- /dev/null +++ b/v3/internal/generator/render/identifier.go @@ -0,0 +1,92 @@ +package render + +import ( + "slices" +) + +// jsid escapes identifiers that match JS/TS reserved words +// by prepending a dollar sign. +func jsid(ident string) string { + if _, reserved := slices.BinarySearch(protectedWords, ident); reserved { + return "$" + ident + } + return ident +} + +func init() { + // Ensure reserved words are sorted in ascending lexicographical order. + slices.Sort(protectedWords) +} + +// protectedWords is a list of JS + TS words that are either reserved +// or have special meaning. Keep in ascending lexicographical order +// for best startup performance. +var protectedWords = []string{ + "JSON", + "Object", + "any", + "arguments", + "as", + "async", + "await", + "boolean", + "break", + "case", + "catch", + "class", + "const", + "constructor", + "continue", + "debugger", + "declare", + "default", + "delete", + "do", + "else", + "enum", + "export", + "extends", + "false", + "finally", + "for", + "from", + "function", + "get", + "if", + "implements", + "import", + "in", + "instanceof", + "interface", + "let", + "module", + "namespace", + "new", + "null", + "number", + "of", + "package", + "private", + "protected", + "public", + "require", + "return", + "set", + "static", + "string", + "super", + "switch", + "symbol", + "this", + "throw", + "true", + "try", + "type", + "typeof", + "undefined", + "var", + "void", + "while", + "with", + "yield", +} diff --git a/v3/internal/generator/render/info.go b/v3/internal/generator/render/info.go new file mode 100644 index 000000000..3947f117a --- /dev/null +++ b/v3/internal/generator/render/info.go @@ -0,0 +1,77 @@ +package render + +import ( + "regexp" + "strings" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// modelInfo gathers useful information about a model. +type modelInfo struct { + HasValues bool + IsEnum bool + + IsAlias bool + IsClassAlias bool + IsTypeAlias bool + + IsClassOrInterface bool + IsInterface bool + IsClass bool + + Template struct { + Params string + ParamList string + CreateList string + } +} + +// createParamRegex must match type parameter creation strings as generated by [modelinfo]. +var createParamRegex = regexp.MustCompile(`\$\$createParam[^\s,)]*`) + +// modelinfo gathers and returns useful information about the given model. +func modelinfo(model *collect.ModelInfo, useInterfaces bool) (info modelInfo) { + info.HasValues = len(model.Values) > 0 + info.IsEnum = info.HasValues && !model.Alias + + info.IsAlias = !info.IsEnum && model.Type != nil + info.IsClassAlias = info.IsAlias && model.Predicates.IsClass && !useInterfaces + info.IsTypeAlias = info.IsAlias && !info.IsClassAlias + + info.IsClassOrInterface = !info.IsEnum && !info.IsAlias + info.IsInterface = info.IsClassOrInterface && (model.Alias || useInterfaces) + info.IsClass = info.IsClassOrInterface && !info.IsInterface + + if len(model.TypeParams) > 0 { + var params, paramList, createList strings.Builder + + paramList.WriteRune('<') + createList.WriteRune('(') + + for i, param := range model.TypeParams { + param = typeparam(i, param) + + if i > 0 { + params.WriteRune(',') + paramList.WriteString(", ") + createList.WriteString(", ") + } + + params.WriteString(param) + paramList.WriteString(param) + + createList.WriteString("$$createParam") + createList.WriteString(param) + } + + paramList.WriteRune('>') + createList.WriteRune(')') + + info.Template.Params = params.String() + info.Template.ParamList = paramList.String() + info.Template.CreateList = createList.String() + } + + return +} diff --git a/v3/internal/generator/render/module.go b/v3/internal/generator/render/module.go new file mode 100644 index 000000000..3611c939a --- /dev/null +++ b/v3/internal/generator/render/module.go @@ -0,0 +1,26 @@ +package render + +import ( + "github.com/wailsapp/wails/v3/internal/flags" + "github.com/wailsapp/wails/v3/internal/generator/collect" + "golang.org/x/tools/go/types/typeutil" +) + +// module gathers data that is used when rendering a single JS/TS module. +type module struct { + *Renderer + *flags.GenerateBindingsOptions + + Imports *collect.ImportMap + + postponedCreates typeutil.Map +} + +// Runtime returns the import path for the Wails JS runtime module. +func (m *module) Runtime() string { + if m.UseBundledRuntime { + return "/wails/runtime.js" + } else { + return "@wailsio/runtime" + } +} diff --git a/v3/internal/generator/render/renderer.go b/v3/internal/generator/render/renderer.go new file mode 100644 index 000000000..5ddee85c2 --- /dev/null +++ b/v3/internal/generator/render/renderer.go @@ -0,0 +1,141 @@ +package render + +import ( + "go/types" + "io" + "slices" + "strings" + "text/template" + + "github.com/wailsapp/wails/v3/internal/flags" + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// Renderer holds the template set for a given configuration. +// It provides methods for rendering various output modules. +type Renderer struct { + options *flags.GenerateBindingsOptions + collector *collect.Collector + + ext string + + service *template.Template + typedefs *template.Template +} + +// NewRenderer initialises a code renderer +// for the given configuration and data collector. +func NewRenderer(options *flags.GenerateBindingsOptions, collector *collect.Collector) *Renderer { + ext := ".js" + if options.TS { + ext = ".ts" + } + + return &Renderer{ + options: options, + collector: collector, + + ext: ext, + + service: tmplService[tmplLanguage(options.TS)], + typedefs: tmplModels[tmplLanguage(options.TS)], + } +} + +// ServiceFile returns the standard name of a service file +// for the given struct name, with the appropriate extension. +func (renderer *Renderer) ServiceFile(name string) string { + return strings.ToLower(name) + renderer.ext +} + +// ModelsFile returns the standard name of a models file +// with the appropriate extension. +func (renderer *Renderer) ModelsFile() string { + return renderer.options.ModelsFilename + renderer.ext +} + +// IndexFile returns the standard name of a package index file +// with the appropriate extension. +func (renderer *Renderer) IndexFile() string { + return renderer.options.IndexFilename + renderer.ext +} + +// Service renders binding code for the given service type to w. +func (renderer *Renderer) Service(w io.Writer, info *collect.ServiceInfo) error { + return renderer.service.Execute(w, &struct { + module + Service *collect.ServiceInfo + }{ + module{ + Renderer: renderer, + GenerateBindingsOptions: renderer.options, + Imports: info.Imports, + }, + info, + }) +} + +// Typedefs renders type definitions for the given list of models. +func (renderer *Renderer) Models(w io.Writer, imports *collect.ImportMap, models []*collect.ModelInfo) error { + if !renderer.options.UseInterfaces { + // Sort class aliases after the class they alias. + // Works in amortized linear time thanks to an auxiliary map. + + // Track postponed class aliases and their dependencies. + aliases := make(map[types.Object][]*collect.ModelInfo, len(models)) + + models = slices.Clone(models) + for i, j := 0, 0; i < len(models); i++ { + if models[i].Type != nil && models[i].Predicates.IsClass { + // models[i] is a class alias: + // models[i].Type is guaranteed to be + // either an alias or a named type + obj := models[i].Type.(interface{ Obj() *types.TypeName }).Obj() + if obj.Pkg().Path() == imports.Self { + // models[i] aliases a type from the current module. + if a, ok := aliases[obj]; !ok || len(a) > 0 { + // The aliased type has not been visited already, postpone. + aliases[obj] = append(a, models[i]) + continue + } + } + } + + // Append models[i]. + models[j] = models[i] + j++ + + // Keep appending aliases whose aliased type has been just appended. + for k := j - 1; k < j; k++ { + a := aliases[models[k].Object()] + aliases[models[k].Object()] = nil // Mark aliased model as visited + j += copy(models[j:], a) + } + } + } + + return renderer.typedefs.Execute(w, &struct { + module + Models []*collect.ModelInfo + }{ + module{ + Renderer: renderer, + GenerateBindingsOptions: renderer.options, + Imports: imports, + }, + models, + }) +} + +// Index renders the given package index to w. +func (renderer *Renderer) Index(w io.Writer, index *collect.PackageIndex) error { + return tmplIndex.Execute(w, &struct { + *collect.PackageIndex + *Renderer + *flags.GenerateBindingsOptions + }{ + index, + renderer, + renderer.options, + }) +} diff --git a/v3/internal/generator/render/templates.go b/v3/internal/generator/render/templates.go new file mode 100644 index 000000000..5aa7398f5 --- /dev/null +++ b/v3/internal/generator/render/templates.go @@ -0,0 +1,39 @@ +package render + +import ( + "embed" + "strings" + "text/template" +) + +//go:embed templates/*.tmpl +var templates embed.FS + +type tmplLanguage bool + +const tmplJS, tmplTS tmplLanguage = false, true + +var tmplService = map[tmplLanguage]*template.Template{ + tmplJS: template.Must(template.New("service.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/service.js.tmpl")), + tmplTS: template.Must(template.New("service.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/service.ts.tmpl")), +} + +var tmplModels = map[tmplLanguage]*template.Template{ + tmplJS: template.Must(template.New("models.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.js.tmpl")), + tmplTS: template.Must(template.New("models.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.ts.tmpl")), +} + +var tmplIndex = template.Must(template.New("index.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/index.tmpl")) + +var Newline string + +func init() { + var builder strings.Builder + + err := template.Must(template.New("newline.tmpl").ParseFS(templates, "templates/newline.tmpl")).Execute(&builder, nil) + if err != nil { + panic(err) + } + + Newline = builder.String() +} diff --git a/v3/internal/generator/render/templates/index.tmpl b/v3/internal/generator/render/templates/index.tmpl new file mode 100644 index 000000000..fd067bf50 --- /dev/null +++ b/v3/internal/generator/render/templates/index.tmpl @@ -0,0 +1,102 @@ +{{$renderer := .}} +{{- $useInterfaces := .UseInterfaces}} +{{- $models := (fixext .ModelsFile)}} +{{- if not .TS -}} +// @ts-check +{{end -}} +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT +{{$hasDocs := false}} +{{- range .Package.Docs}} +{{- if hasdoc .}}{{$hasDocs := true}}{{break}}{{end}} +{{- end}} +{{- if $hasDocs}} +/** +{{- range .Package.Docs}} +{{- jsdoc .Text ""}} +{{- end}} + * @module + */ +{{end}} +{{- if .Services}} +{{- range .Services}}{{if .Internal}}{{break}}{{end}} +import * as {{jsid .Name}} from "./{{js (fixext ($renderer.ServiceFile .Name))}}"; +{{- end}} +export { +{{- range $i, $service := .Services}} + {{- if .Internal}}{{break}}{{end}} + {{- if gt $i 0}},{{end}} + {{jsid .Name}} +{{- end}} +}; +{{end}} + +{{- $hasObjects := false}} +{{- $hasTypes := false}} + +{{- range $model := .Models}} +{{- if $model.Internal}}{{break}}{{end}} + +{{- $info := modelinfo $model $useInterfaces }} + +{{- if or $info.HasValues $info.IsClassAlias $info.IsClass}} +{{- if not $hasObjects}} + {{- $hasObjects = true}} +export { +{{- else}},{{end}} + {{jsid $model.Name}} +{{- else}} + {{- $hasTypes = true}} +{{- end}} +{{- end}} +{{- if $hasObjects}} +} from "./{{js $models}}"; +{{end}} + +{{- if $hasTypes}} +{{- $hasTypes = false}} + +{{- if .TS}} +export type { +{{- else}} +import * as $models from "./{{js $models}}"; +{{end}} +{{- range $model := .Models}} +{{- if $model.Internal}}{{break}}{{end}} + +{{- $info := modelinfo $model $useInterfaces }} +{{- $template := $info.Template }} + +{{- if or $info.HasValues $info.IsClassAlias $info.IsClass}}{{continue}}{{end}} + +{{- if $renderer.TS}} + {{- if $hasTypes}},{{end}} + {{jsid $model.Name}} +{{- else}} +/** +{{- if hasdoc $model.Decl.Doc}} +{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}} + *{{end}} +{{- end}} +{{- if hasdoc $model.Doc}} +{{- jsdoc $model.Doc.Text ""}} +{{- end}} +{{- if $template.ParamList}} + * @template {{$template.Params}} +{{- end}} + * @typedef {$models.{{jsid $model.Name}}{{$template.ParamList -}} } {{jsid $model.Name}} + */ +{{end}} + +{{- $hasTypes = true}} +{{- end}} + +{{- if .TS}} +} from "./{{js $models}}"; +{{end}} + +{{- end}} +{{- range .Package.Injections}} +{{.}} +{{- end}}{{if .Package.Injections}} +{{end -}} diff --git a/v3/internal/generator/render/templates/models.js.tmpl b/v3/internal/generator/render/templates/models.js.tmpl new file mode 100644 index 000000000..6c0e98b8b --- /dev/null +++ b/v3/internal/generator/render/templates/models.js.tmpl @@ -0,0 +1,207 @@ +{{$module := .}} +{{- $runtime := $module.Runtime}} +{{- $models := (fixext $module.ModelsFile)}} +{{- $useInterfaces := .UseInterfaces}} +{{- $imports := $module.Imports -}} +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT +{{if not $useInterfaces}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Create as $Create } from "{{js $runtime}}"; +{{end -}} +{{range $imports.External}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}"; +{{- end}}{{if $imports.External}} +{{end}} +{{- range $model := .Models}} + +{{- $info := modelinfo $model $useInterfaces }} +{{- $template := $info.Template }} + +{{- if or $template.ParamList (hasdoc $model.Decl.Doc) (hasdoc $model.Doc) $info.IsEnum $info.IsTypeAlias $info.IsInterface}} +/** +{{- if hasdoc $model.Decl.Doc}} +{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}} + *{{end}} +{{- end}} +{{- if hasdoc $model.Doc}} +{{- jsdoc $model.Doc.Text ""}} +{{- end}} +{{- if and $template.ParamList (not $info.IsClassAlias)}} + * @template {{$template.Params}} +{{- end}} +{{- if $info.IsEnum}} + * @readonly + * @enum { {{- $module.JSType $model.Type -}} } +{{- else if $info.IsTypeAlias}} + * @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}} +{{- else if $info.IsInterface}} +{{- if isjsdocobj $model}} + * @typedef {Object} {{jsid $model.Name}} +{{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}} + * @property { {{- $module.JSFieldType $field.StructField -}} } + {{- if $field.Optional}} [{{else}} {{end}}{{$field.JsonName}}{{if $field.Optional}}]{{end}} + {{- if hasdoc $field.Decl.Doc}} - {{jsdocline $field.Decl.Doc.Text}}{{end}} +{{- end}}{{end}} +{{- else}} + * @typedef { { +{{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}} + * "{{js $field.JsonName}}"{{if $field.Optional}}?{{end}}: {{$module.JSFieldType $field.StructField}}, +{{- end}}{{end}} + * } } {{jsid $model.Name}} +{{- end}} +{{- end}} + */ +{{- end}} +{{- if $info.HasValues}} +{{- if not $info.IsEnum}} + +/** + * Predefined constants for type {{jsid $model.Name}}. + * @namespace + */ +{{- end}} +export const {{jsid $model.Name}} = { +{{- if $info.IsEnum}} + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: {{$module.JSDefault $model.Type false}}, +{{end}} +{{- range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}} + {{- if and (ne $i 0) (eq $j 0) (eq $k 0)}} +{{end}} + {{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}} + {{- if gt $j 0}} +{{end}} + /** + {{- if and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)}} + {{- jsdoc $value.Decl.Doc.Text " "}}{{if and (eq $k 0) (hasdoc $value.Spec.Doc)}} + *{{end}} + {{- end}} + {{- if and (eq $k 0) (hasdoc $value.Spec.Doc)}} + {{- jsdoc $value.Spec.Doc.Text " "}} + {{- end}} + */ + {{- end}} + {{jsid $value.Name}}: {{jsvalue $value.Value}}, +{{- end}}{{end}}{{end}} +}; +{{else if $info.IsClassAlias}} +export const {{jsid $model.Name}} = {{if istpalias $model.Type -}} + {{$module.JSType (unalias $model.Type).Origin}}; +{{- else -}} + {{$module.JSType $model.Type.Origin}}; +{{- end}} + +/** +{{- if hasdoc $model.Decl.Doc}} +{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}} + *{{end}} +{{- end}} +{{- if hasdoc $model.Doc}} +{{- jsdoc $model.Doc.Text ""}} +{{- end}} +{{- if $template.ParamList}} + * @template {{$template.Params}} +{{- end}} + * @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}} + */ +{{else if and $info.IsClass}} +export class {{jsid $model.Name}} { + /** + * Creates a new {{jsid $model.Name}} instance. + * @param {Partial<{{jsid $model.Name}}{{$template.ParamList}}>} [$$source = {}] - The source object to create the {{jsid $model.Name}}. + */ + constructor($$source = {}) { + {{- range $decl := $model.Fields}}{{range $j, $field := $decl}} + {{- /* + In JS we need to set all properties explicitly + because JSDoc has no support for arbitrary property names yet. + See https://github.com/jsdoc/jsdoc/issues/1468 + + For optional fields we make the initialization code unreachable + and cast the false condition to any to prevent any complaint from Typescript. + */}} + if ({{if $field.Optional}}/** @type {any} */(false){{else}}!("{{js $field.JsonName}}" in $$source){{end}}) { + /** + {{- if and (eq $j 0) (hasdoc $field.Decl.Doc)}} + {{- jsdoc $field.Decl.Doc.Text " "}} + {{- end}} + * @member + * @type { {{- $module.JSFieldType $field.StructField}}{{if $field.Optional}} | undefined{{end -}} } + */ + this["{{js $field.JsonName}}"] = {{if $field.Optional}}undefined{{else}}{{$module.JSDefault $field.Type $field.Quoted}}{{end}}; + } + {{- end}}{{end}} + + Object.assign(this, $$source); + } + + /** + {{- if $template.ParamList}} + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class {{jsid $model.Name}}. + {{- range $i, $param := $model.TypeParams}} + {{- $param = (typeparam $i $param)}} + * @template [{{$param}}=any] + {{- end}} + {{- range $i, $param := $model.TypeParams}} + {{- $param = (typeparam $i $param)}} + * @param {(source: any) => {{$param -}} } $$createParam{{$param}} + {{- end}} + * @returns {($$source?: any) => {{jsid $model.Name}}{{$template.ParamList -}} } + {{- else}} + * Creates a new {{jsid $model.Name}} instance from a string or object. + * @param {any} [$$source = {}] + * @returns { {{- jsid $model.Name -}} } + {{- end}} + */ + static createFrom{{if $template.ParamList}}{{$template.CreateList}}{{else}}($$source = {}){{end}} { + {{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}} + {{- $create := ($module.JSCreateWithParams $field.Type $template.CreateList)}} + {{- if ne $create "$Create.Any"}} + const $$createField{{$i}}_{{$j}} = {{$create}}; + {{- end}} + {{- end}}{{end}} + {{- $indent := ""}} + {{- if $template.ParamList}} + {{- $indent = " "}} + return ($$source = {}) => { + {{- end}} + {{$indent}}let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + {{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}} + {{- if $module.NeedsCreate $field.Type}} + {{$indent}}if ("{{js $field.JsonName}}" in $$parsedSource) { + {{$indent}} $$parsedSource["{{js $field.JsonName}}"] = $$createField{{$i}}_{{$j}}($$parsedSource["{{js $field.JsonName}}"]); + {{$indent -}} } + {{- end}} + {{- end}}{{end}} + {{$indent}}return new {{jsid $model.Name}}(/** @type {Partial<{{jsid $model.Name}}{{$template.ParamList}}>} */($$parsedSource)); + {{- if $template.ParamList}} + }; + {{- end}} + } +} +{{else}} +{{- /* Rendered as a @typedef */}} +{{end}} +{{- end}} +{{- $postponed := $module.PostponedCreates}} +{{- if $postponed}} +// Private type creation functions +{{- range $i, $create := $postponed}} +{{if and (ge (len $create) 54) (eq (slice $create 39 54) "function $$init")}}var {{else}}const {{end -}} +$$createType{{$i}} = {{$create}}; +{{- end}} +{{end}} +{{- if $useInterfaces}} +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; +{{end -}} diff --git a/v3/internal/generator/render/templates/models.ts.tmpl b/v3/internal/generator/render/templates/models.ts.tmpl new file mode 100644 index 000000000..a6073af08 --- /dev/null +++ b/v3/internal/generator/render/templates/models.ts.tmpl @@ -0,0 +1,189 @@ +{{$module := .}} +{{- $runtime := $module.Runtime}} +{{- $models := (fixext $module.ModelsFile)}} +{{- $useInterfaces := .UseInterfaces}} +{{- $imports := $module.Imports -}} +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT +{{if not $useInterfaces}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Create as $Create } from "{{js $runtime}}"; +{{end -}} +{{range $imports.External}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}"; +{{- end}}{{if $imports.External}} +{{end}} +{{- range $model := .Models}} + +{{- $info := modelinfo $model $useInterfaces }} +{{- $template := $info.Template }} + +{{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}} +/** +{{- if hasdoc $model.Decl.Doc}} +{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}} + *{{end}} +{{- end}} +{{- if hasdoc $model.Doc}} +{{- jsdoc $model.Doc.Text ""}} +{{- end}} + */ +{{- end}} +{{- if $info.IsEnum}} +export enum {{jsid $model.Name}} { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = {{$module.JSDefault $model.Type false}}, +{{range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}} + {{- if and (ne $i 0) (eq $j 0) (eq $k 0)}} +{{end}} + {{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}} + {{- if gt $j 0}} +{{end}} + /** + {{- if and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)}} + {{- jsdoc $value.Decl.Doc.Text " "}}{{if and (eq $k 0) (hasdoc $value.Spec.Doc)}} + *{{end}} + {{- end}} + {{- if and (eq $k 0) (hasdoc $value.Spec.Doc)}} + {{- jsdoc $value.Spec.Doc.Text " "}} + {{- end}} + */ + {{- end}} + {{jsid $value.Name}} = {{jsvalue $value.Value}}, + {{- end}}{{end}}{{end}} +}; +{{else if $info.IsClassAlias}} +export const {{jsid $model.Name}} = {{if istpalias $model.Type -}} + {{$module.JSType (unalias $model.Type).Origin}}; +{{- else -}} + {{$module.JSType $model.Type.Origin}}; +{{- end}} +{{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}} + +/** +{{- if hasdoc $model.Decl.Doc}} +{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}} + *{{end}} +{{- end}} +{{- if hasdoc $model.Doc}} +{{- jsdoc $model.Doc.Text ""}} +{{- end}} + */ +{{- end}} +export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}}; +{{else if $info.IsTypeAlias}} +export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}}; +{{- if $info.HasValues}} + +/** + * Predefined constants for type {{jsid $model.Name}}. + * @namespace + */ +export const {{jsid $model.Name}} = { +{{- range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}} + {{- if and (ne $i 0) (eq $j 0) (eq $k 0)}} +{{end}} + {{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}} + {{- if gt $j 0}} +{{end}} + /** + {{- if and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)}} + {{- jsdoc $value.Decl.Doc.Text " "}}{{if and (eq $k 0) (hasdoc $value.Spec.Doc)}} + *{{end}} + {{- end}} + {{- if and (eq $k 0) (hasdoc $value.Spec.Doc)}} + {{- jsdoc $value.Spec.Doc.Text " "}} + {{- end}} + */ + {{- end}} + {{jsid $value.Name}}: {{jsvalue $value.Value}}, +{{- end}}{{end}}{{end}} +}; +{{- end}} +{{else if $info.IsClassOrInterface}} +export {{if $info.IsInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$template.ParamList}} { + {{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}} + {{- if and (eq $j 0) (hasdoc $field.Decl.Doc)}} + {{- if gt $i 0}} +{{end}} + /** + {{- jsdoc $field.Decl.Doc.Text " "}} + */ + {{- end}} + "{{js $field.JsonName}}"{{if $field.Optional}}?{{end}}: {{$module.JSFieldType $field.StructField}}; + {{- end}}{{end}} +{{- if $info.IsClass}} + + /** Creates a new {{jsid $model.Name}} instance. */ + constructor($$source: Partial<{{jsid $model.Name}}{{$template.ParamList}}> = {}) { + {{- range $spec := $model.Fields}}{{range $i, $field := $spec}}{{if not $field.Optional}} + if (!("{{js $field.JsonName}}" in $$source)) { + this["{{js $field.JsonName}}"] = {{$module.JSDefault $field.Type $field.Quoted}}; + } + {{- end}}{{end}}{{end}} + + Object.assign(this, $$source); + } + + /** + {{- if $template.ParamList}} + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class {{jsid $model.Name}}. + {{- else}} + * Creates a new {{jsid $model.Name}} instance from a string or object. + {{- end}} + */ + static createFrom{{if $template.ParamList}}< + {{- range $i, $param := $model.TypeParams}} + {{- $param = (typeparam $i $param)}} + {{- if gt $i 0}}, {{end -}} + {{$param}} = any + {{- end}}>{{end}}({{if $template.ParamList}} + {{- range $i, $param := $model.TypeParams}} + {{- $param = (typeparam $i $param)}} + {{- if gt $i 0}}, {{end -}} + $$createParam{{$param}}: (source: any) => {{$param}}{{end -}} + {{else}}$$source: any = {}{{end}}): + {{- if $template.ParamList}} ($$source?: any) =>{{end}} {{jsid $model.Name}}{{$template.ParamList}} { + {{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}} + {{- $create := ($module.JSCreateWithParams $field.Type $template.CreateList)}} + {{- if ne $create "$Create.Any"}} + const $$createField{{$i}}_{{$j}} = {{$create}}; + {{- end}} + {{- end}}{{end}} + {{- $indent := ""}} + {{- if $template.ParamList}} + {{- $indent = " "}} + return ($$source: any = {}) => { + {{- end}} + {{$indent}}let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + {{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}} + {{- if $module.NeedsCreate $field.Type}} + {{$indent}}if ("{{js $field.JsonName}}" in $$parsedSource) { + {{$indent}} $$parsedSource["{{js $field.JsonName}}"] = $$createField{{$i}}_{{$j}}($$parsedSource["{{js $field.JsonName}}"]); + {{$indent -}} } + {{- end}} + {{- end}}{{end}} + {{$indent}}return new {{jsid $model.Name}}{{$template.ParamList}}($$parsedSource as Partial<{{jsid $model.Name}}{{$template.ParamList}}>); + {{- if $template.ParamList}} + }; + {{- end}} + } +{{- end}} +} +{{end}} +{{- end}} +{{- $postponed := $module.PostponedCreates}} +{{- if $postponed}} +// Private type creation functions +{{- range $i, $create := $postponed}} +{{if and (ge (len $create) 16) (eq (slice $create 1 16) "function $$init")}}var {{else}}const {{end -}} +$$createType{{$i}} = {{$create}}; +{{- end}} +{{end -}} diff --git a/v3/internal/generator/render/templates/newline.tmpl b/v3/internal/generator/render/templates/newline.tmpl new file mode 100644 index 000000000..d28790668 --- /dev/null +++ b/v3/internal/generator/render/templates/newline.tmpl @@ -0,0 +1,6 @@ +{{/* This template should render to a single newline (either LF or CRLF). */}} +{{/* + Git might be configured to rewrite LF newlines to CRLF + in templates, test cases and data, especially on Windows. + Having a newline template enables detection of the current newline mode. +*/ -}} diff --git a/v3/internal/generator/render/templates/service.js.tmpl b/v3/internal/generator/render/templates/service.js.tmpl new file mode 100644 index 000000000..f71e055bf --- /dev/null +++ b/v3/internal/generator/render/templates/service.js.tmpl @@ -0,0 +1,100 @@ +{{$module := .}} +{{- $runtime := $module.Runtime}} +{{- $models := (fixext $module.ModelsFile)}} +{{- $useNames := $module.UseNames}} +{{- $useInterfaces := $module.UseInterfaces}} +{{- $imports := $module.Imports}} +{{- with .Service -}} +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT +{{if or (hasdoc .Decl.Doc) (hasdoc .Doc)}} +/** +{{- if hasdoc .Decl.Doc}} +{{- jsdoc .Decl.Doc.Text ""}}{{if hasdoc .Doc}} + *{{end}} +{{- end}} +{{- if hasdoc .Doc}} +{{- jsdoc .Doc.Text ""}} +{{- end}} + * @module + */ +{{end}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise{{if not $useInterfaces}}, Create as $Create{{end}} } from "{{js $runtime}}"; +{{range $imports.External}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}"; +{{- end}}{{if $imports.External}} +{{end}} +{{- if $imports.ImportModels}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./{{js $models}}"; +{{end}} +{{- range .Methods}} +/** +{{- if hasdoc .Decl.Doc}} +{{- jsdoc .Decl.Doc.Text ""}}{{if hasdoc .Doc}} + *{{end}} +{{- end}} +{{- if hasdoc .Doc}} +{{- jsdoc .Doc.Text ""}} +{{- end}} +{{- range $i, $param := .Params}} + * @param { {{- $module.JSType .Type}}{{if .Variadic}}[]{{end -}} } {{jsparam $i .}} +{{- end}} + * @returns {$CancellablePromise< + {{- if eq 0 (len .Results) -}} + void + {{- else if eq 1 (len .Results)}} + {{- $module.JSType (index .Results 0)}} + {{- else -}} + [{{range $i, $result := .Results}} + {{- if gt $i 0}}, {{end}} + {{- $module.JSType $result}} + {{- end}}] + {{- end}}>} + */ +{{if not .Internal}}export {{end}}function {{.Name}}({{range $i, $param := .Params -}} + {{- if gt $i 0}}, {{end}} + {{- if .Variadic}}...{{end}} + {{- jsparam $i .}} +{{- end}}) { + {{- if $useNames}} + return $Call.ByName("{{js .FQN}}" + {{- else}} + return $Call.ByID({{.ID}} + {{- end}}{{range $i, $param := .Params}}, {{jsparam $i .}}{{end}}) + {{- if or $useInterfaces (not .Results) ($module.SkipCreate .Results) -}} + ; + {{- else -}} + .then(/** @type {($result: any) => any} */(($result) => { + {{- if eq 1 (len .Results)}} + return {{$module.JSCreate (index .Results 0)}}($result); + {{- else}} + {{- range $i, $type := .Results}} + {{- $create := ($module.JSCreate $type)}} + {{- if ne $create "$Create.Any"}} + $result[{{$i}}] = {{$create}}($result[{{$i}}]); + {{- end}}{{end}} + return $result; + {{- end}} + })); + {{- end}} +} +{{end}} +{{- $postponed := $module.PostponedCreates}} +{{- if $postponed}} +// Private type creation functions +{{- range $i, $create := $postponed}} +{{if and (ge (len $create) 54) (eq (slice $create 39 54) "function $$init")}}var {{else}}const {{end -}} +$$createType{{$i}} = {{$create}}; +{{- end}} +{{end}} +{{- range .Injections}} +{{.}} +{{- end}}{{if .Injections}} +{{end}}{{end -}} diff --git a/v3/internal/generator/render/templates/service.ts.tmpl b/v3/internal/generator/render/templates/service.ts.tmpl new file mode 100644 index 000000000..4db90fe60 --- /dev/null +++ b/v3/internal/generator/render/templates/service.ts.tmpl @@ -0,0 +1,97 @@ +{{$module := .}} +{{- $runtime := $module.Runtime}} +{{- $models := (fixext $module.ModelsFile)}} +{{- $useNames := $module.UseNames}} +{{- $useInterfaces := $module.UseInterfaces}} +{{- $imports := $module.Imports}} +{{- with .Service -}} +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT +{{if or (hasdoc .Decl.Doc) (hasdoc .Doc)}} +/** +{{- if hasdoc .Decl.Doc}} +{{- jsdoc .Decl.Doc.Text ""}}{{if hasdoc .Doc}} + *{{end}} +{{- end}} +{{- if hasdoc .Doc}} +{{- jsdoc .Doc.Text ""}} +{{- end}} + * @module + */ +{{end}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise{{if not $useInterfaces}}, Create as $Create{{end}} } from "{{js $runtime}}"; +{{range $imports.External}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}"; +{{- end}}{{if $imports.External}} +{{end}} +{{- if $imports.ImportModels}} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./{{js $models}}"; +{{end}} +{{- range .Methods}} +{{- if or (hasdoc .Decl.Doc) (hasdoc .Doc)}} +/** +{{- if hasdoc .Decl.Doc}} +{{- jsdoc .Decl.Doc.Text ""}}{{if hasdoc .Doc}} + *{{end}} +{{- end}} +{{- if .Doc}} +{{- jsdoc .Doc.Text ""}} +{{- end}} + */ +{{- end}} +{{if not .Internal}}export {{end}}function {{.Name}}({{range $i, $param := .Params -}} + {{- if gt $i 0}}, {{end}} + {{- if .Variadic}}...{{end}} + {{- jsparam $i .}}: {{$module.JSType .Type}}{{if .Variadic}}[]{{end}} +{{- end}}): $CancellablePromise< + {{- if eq 0 (len .Results) -}} + void + {{- else if eq 1 (len .Results)}} + {{- $module.JSType (index .Results 0)}} + {{- else -}} + [{{range $i, $result := .Results}} + {{- if gt $i 0}}, {{end}} + {{- $module.JSType $result}} + {{- end}}] + {{- end}}> { + {{- if $useNames}} + return $Call.ByName("{{js .FQN}}" + {{- else}} + return $Call.ByID({{.ID}} + {{- end}}{{range $i, $param := .Params}}, {{jsparam $i .}}{{end}}) + {{- if or $useInterfaces (not .Results) ($module.SkipCreate .Results) -}} + ; + {{- else -}} + .then(($result: any) => { + {{- if eq 1 (len .Results)}} + return {{$module.JSCreate (index .Results 0)}}($result); + {{- else}} + {{- range $i, $type := .Results}} + {{- $create := ($module.JSCreate $type)}} + {{- if ne $create "$Create.Any"}} + $result[{{$i}}] = {{$create}}($result[{{$i}}]); + {{- end}}{{end}} + return $result; + {{- end}} + }); + {{- end}} +} +{{end}} +{{- $postponed := $module.PostponedCreates}} +{{- if $postponed}} +// Private type creation functions +{{- range $i, $create := $postponed}} +{{if and (ge (len $create) 16) (eq (slice $create 1 16) "function $$init")}}var {{else}}const {{end -}} +$$createType{{$i}} = {{$create}}; +{{- end}} +{{end}} +{{- range .Injections}} +{{.}} +{{- end}}{{if .Injections}} +{{end}}{{end -}} diff --git a/v3/internal/generator/render/type.go b/v3/internal/generator/render/type.go new file mode 100644 index 000000000..2c880154a --- /dev/null +++ b/v3/internal/generator/render/type.go @@ -0,0 +1,265 @@ +package render + +import ( + "fmt" + "go/types" + "strings" + "text/template" + + "github.com/wailsapp/wails/v3/internal/generator/collect" +) + +// aliasOrNamed is a common interface for *types.Alias and *types.Named. +type aliasOrNamed interface { + types.Type + Obj() *types.TypeName +} + +// typeByteSlice caches the type-checker type for a slice of bytes. +var typeByteSlice = types.NewSlice(types.Universe.Lookup("byte").Type()) + +// JSType renders a Go type to its TypeScript representation, +// using the receiver's import map to resolve dependencies. +// +// JSType's output may be incorrect if m.Imports.AddType +// has not been called for the given type. +func (m *module) JSType(typ types.Type) string { + result, _ := m.renderType(typ, false) + return result +} + +// JSFieldType renders a struct field type to its TypeScript representation, +// using the receiver's import map to resolve dependencies. +// +// JSFieldType's output may be incorrect if m.Imports.AddType +// has not been called for the given type. +func (m *module) JSFieldType(field *collect.StructField) string { + result, _ := m.renderType(field.Type, field.Quoted) + return result +} + +// renderType provides the actual implementation of [module.Type]. +// It returns the rendered type and a boolean indicating whether +// the resulting expression describes a nullable type. +func (m *module) renderType(typ types.Type, quoted bool) (result string, nullable bool) { + switch t := typ.(type) { + case *types.Alias, *types.Named: + return m.renderNamedType(typ.(aliasOrNamed), quoted) + + case *types.Array, *types.Slice: + null := "" + if _, isSlice := typ.(*types.Slice); isSlice && m.UseInterfaces { + // In interface mode, record the fact that encoding/json marshals nil slices as null. + null = " | null" + } + + if types.Identical(typ, typeByteSlice) { + // encoding/json marshals byte slices as base64 strings + return "string" + null, null != "" + } + + elem, ptr := m.renderType(typ.(interface{ Elem() types.Type }).Elem(), false) + if ptr { + return fmt.Sprintf("(%s)[]%s", elem, null), null != "" + } else { + return fmt.Sprintf("%s[]%s", elem, null), null != "" + } + + case *types.Basic: + return m.renderBasicType(t, quoted), false + + case *types.Map: + return m.renderMapType(t) + + case *types.Pointer: + elem, nullable := m.renderType(t.Elem(), false) + if nullable { + return elem, nullable + } else { + return fmt.Sprintf("%s | null", elem), true + } + + case *types.Struct: + return m.renderStructType(t), false + + case *types.TypeParam: + pre, post := "", "" + if quoted { + pre, post = "(", " | string)" + } + return fmt.Sprintf("%s%s%s", pre, typeparam(t.Index(), t.Obj().Name()), post), false + } + + // Fall back to untyped mode. + return "any", false +} + +// renderBasicType outputs the TypeScript representation +// of the given basic type. +func (*module) renderBasicType(typ *types.Basic, quoted bool) string { + switch { + case typ.Info()&types.IsBoolean != 0: + if quoted { + return "`${boolean}`" + } else { + return "boolean" + } + + case typ.Info()&types.IsNumeric != 0 && typ.Info()&types.IsComplex == 0: + if quoted { + return "`${number}`" + } else { + return "number" + } + + case typ.Info()&types.IsString != 0: + if quoted { + return "`\"${string}\"`" + } else { + return "string" + } + } + + // Fall back to untyped mode. + if quoted { + return "string" + } else { + return "any" + } +} + +// renderMapType outputs the TypeScript representation of the given map type. +func (m *module) renderMapType(typ *types.Map) (result string, nullable bool) { + null := "" + if m.UseInterfaces { + // In interface mode, record the fact that encoding/json marshals nil slices as null. + null = " | null" + } + + key := "string" + elem, _ := m.renderType(typ.Elem(), false) + + // Test whether we can upgrade key rendering. + switch k := typ.Key().(type) { + case *types.Basic: + if k.Info()&types.IsString == 0 && collect.IsMapKey(k) { + // Render non-string basic type in quoted mode. + key = m.renderBasicType(k, true) + } + + case *types.Alias, *types.Named, *types.Pointer: + if collect.IsMapKey(k) { + if collect.IsStringAlias(k) { + // Alias or named type is a string and therefore + // safe to use as a JS object key. + if ptr, ok := k.(*types.Pointer); ok { + // Unwrap pointers to string aliases. + key, _ = m.renderType(ptr.Elem(), false) + } else { + key, _ = m.renderType(k, false) + } + } else if basic, ok := k.Underlying().(*types.Basic); ok && basic.Info()&types.IsString == 0 { + // Render non-string basic type in quoted mode. + key = m.renderBasicType(basic, true) + } + } + } + + return fmt.Sprintf("{ [_: %s]: %s }%s", key, elem, null), m.UseInterfaces +} + +// renderNamedType outputs the TS representation +// of the given named or alias type. +func (m *module) renderNamedType(typ aliasOrNamed, quoted bool) (result string, nullable bool) { + if typ.Obj().Pkg() == nil { + // Builtin alias or named type: render underlying type. + return m.renderType(typ.Underlying(), quoted) + } + + if quoted { + switch a := types.Unalias(typ).(type) { + case *types.Basic: + // Quoted mode for (alias of?) basic type: delegate. + return m.renderBasicType(a, quoted), false + case *types.TypeParam: + // Quoted mode for (alias of?) typeparam: delegate. + return m.renderType(a, quoted) + case *types.Named: + // Quoted mode for (alias of?) named type. + // WARN: Do not test with IsAny/IsStringAlias here!! We only want to catch marshalers. + if collect.MaybeJSONMarshaler(typ) == collect.NonMarshaler && collect.MaybeTextMarshaler(typ) == collect.NonMarshaler { + // No custom marshaling for this type. + if u, ok := a.Underlying().(*types.Basic); ok { + // Quoted mode for basic named type that is not a marshaler: delegate. + return m.renderBasicType(u, quoted), false + } + } + } + } + + var builder strings.Builder + + if typ.Obj().Pkg().Path() == m.Imports.Self { + if m.Imports.ImportModels { + builder.WriteString("$models.") + } + } else { + builder.WriteString(jsimport(m.Imports.External[typ.Obj().Pkg().Path()])) + builder.WriteRune('.') + } + builder.WriteString(jsid(typ.Obj().Name())) + + instance, _ := typ.(interface{ TypeArgs() *types.TypeList }) + if instance != nil { + // Render type arguments. + if targs := instance.TypeArgs(); targs != nil && targs.Len() > 0 { + builder.WriteRune('<') + for i := range targs.Len() { + if i > 0 { + builder.WriteString(", ") + } + arg, _ := m.renderType(targs.At(i), false) + builder.WriteString(arg) + } + builder.WriteRune('>') + } + } + + return builder.String(), false +} + +// renderStructType outputs the TS representation +// of the given anonymous struct type. +func (m *module) renderStructType(typ *types.Struct) string { + if collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler { + return "any" + } else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler { + return "string" + } + + info := m.collector.Struct(typ) + info.Collect() + + var builder strings.Builder + + builder.WriteRune('{') + for i, field := range info.Fields { + if i > 0 { + builder.WriteString(", ") + } + + builder.WriteRune('"') + template.JSEscape(&builder, []byte(field.JsonName)) + builder.WriteRune('"') + + if field.Optional { + builder.WriteRune('?') + } + + builder.WriteString(": ") + builder.WriteString(m.JSFieldType(field)) + } + builder.WriteRune('}') + + return builder.String() +} diff --git a/v3/internal/generator/service.go b/v3/internal/generator/service.go new file mode 100644 index 000000000..fcbfa9447 --- /dev/null +++ b/v3/internal/generator/service.go @@ -0,0 +1,102 @@ +package generator + +import ( + "go/types" + "path/filepath" +) + +// generateService collects information +// and generates JS/TS binding code +// for the given service type object. +func (generator *Generator) generateService(obj *types.TypeName) { + generator.logger.Debugf( + "discovered service type %s from package %s", + obj.Name(), + obj.Pkg().Path(), + ) + + success := false + defer func() { + if !success { + generator.logger.Errorf( + "package %s: type %s: service code generation failed", + obj.Pkg().Path(), + obj.Name(), + ) + } + }() + + // Collect service information. + info := generator.collector.Service(obj).Collect() + if info == nil { + return + } + + if info.IsEmpty() { + if !info.HasInternalMethods { + generator.logger.Infof( + "package %s: type %s: service has no valid exported methods, skipping", + obj.Pkg().Path(), + obj.Name(), + ) + } + success = true + return + } + + // Check for standard filename collisions. + filename := generator.renderer.ServiceFile(info.Name) + switch filename { + case generator.renderer.ModelsFile(): + generator.logger.Errorf( + "package %s: type %s: service filename collides with models filename; please rename the type or choose a different filename for models", + obj.Pkg().Path(), + obj.Name(), + ) + return + + case generator.renderer.IndexFile(): + if !generator.options.NoIndex { + generator.logger.Errorf( + "package %s: type %s: service filename collides with JS/TS index filename; please rename the type or choose a different filename for JS/TS indexes", + obj.Pkg().Path(), + obj.Name(), + ) + return + } + } + + // Check for upper/lower-case filename collisions. + path := filepath.Join(info.Imports.Self, filename) + if other, present := generator.serviceFiles.LoadOrStore(path, obj); present { + generator.logger.Errorf( + "package %s: type %s: service filename collides with filename for service %s; please avoid multiple services whose names differ only in case", + obj.Pkg().Path(), + obj.Name(), + other.(*types.TypeName).Name(), + ) + return + } + + // Create service file. + file, err := generator.creator.Create(path) + if err != nil { + generator.logger.Errorf("%v", err) + return + } + defer func() { + if err := file.Close(); err != nil { + generator.logger.Errorf("%v", err) + success = false + } + }() + + // Render service code. + err = generator.renderer.Service(file, info) + if err != nil { + generator.logger.Errorf("%v", err) + return + } + + success = true +} diff --git a/v3/internal/generator/testcases/aliases/bound_types.json b/v3/internal/generator/testcases/aliases/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/aliases/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/aliases/main.go b/v3/internal/generator/testcases/aliases/main.go new file mode 100644 index 000000000..f50d1634a --- /dev/null +++ b/v3/internal/generator/testcases/aliases/main.go @@ -0,0 +1,137 @@ +package main + +import ( + _ "embed" + "encoding" + "log" + + nobindingshere "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here" + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService int + +// A nice type Alias. +type Alias = int + +// A class alias. +type AliasedPerson = Person + +// An empty struct alias. +type EmptyAliasStruct = struct{} + +// A struct alias. +// This should be rendered as a typedef or interface in every mode. +type AliasStruct = struct { + // A field with a comment. + Foo []int + Bar, Baz string `json:",omitempty"` // Definitely not Foo. + + Other OtherAliasStruct // A nested alias struct. +} + +// Another struct alias. +type OtherAliasStruct = struct { + NoMoreIdeas []rune +} + +// An empty struct. +type EmptyStruct struct{} + +// A non-generic struct containing an alias. +type Person struct { + Name string // The Person's name. + AliasedField Alias // A random alias field. +} + +// A generic struct containing an alias. +type GenericPerson[T any] struct { + Name T + AliasedField Alias +} + +// Another class alias, but ordered after its aliased class. +type StrangelyAliasedPerson = Person + +// A generic alias that forwards to a type parameter. +type GenericAlias[T any] = T + +// A generic alias that wraps a pointer type. +type GenericPtrAlias[T any] = *GenericAlias[T] + +// A generic alias that wraps a map. +type GenericMapAlias[T interface { + comparable + encoding.TextMarshaler +}, U any] = map[T]U + +// A generic alias that wraps a generic struct. +type GenericPersonAlias[T any] = GenericPerson[[]GenericPtrAlias[T]] + +// An alias that wraps a class through a non-typeparam alias. +type IndirectPersonAlias = GenericPersonAlias[bool] + +// An alias that wraps a class through a typeparam alias. +type TPIndirectPersonAlias = GenericAlias[GenericPerson[bool]] + +// A class whose fields have various aliased types. +type AliasGroup struct { + GAi GenericAlias[int] + GAP GenericAlias[GenericPerson[bool]] + GPAs GenericPtrAlias[[]string] + GPAP GenericPtrAlias[GenericPerson[[]int]] + GMA GenericMapAlias[struct{ encoding.TextMarshaler }, float32] + GPA GenericPersonAlias[bool] + IPA IndirectPersonAlias + TPIPA TPIndirectPersonAlias +} + +// Get someone. +func (GreetService) Get(aliasValue Alias) Person { + return Person{"hello", aliasValue} +} + +// Get someone quite different. +func (GreetService) GetButDifferent() GenericPerson[bool] { + return GenericPerson[bool]{true, 13} +} + +// Apparently, aliases are all the rage right now. +func (GreetService) GetButAliased(p AliasedPerson) StrangelyAliasedPerson { + return p +} + +func (GreetService) GetButForeignPrivateAlias() (_ nobindingshere.PrivatePerson) { + return +} + +func (GreetService) GetButGenericAliases() (_ AliasGroup) { + return +} + +// Greet a lot of unusual things. +func (GreetService) Greet(EmptyAliasStruct, EmptyStruct) AliasStruct { + return AliasStruct{} +} + +func NewGreetService() application.Service { + return application.NewService(new(GreetService)) +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/complex_expressions/bound_types.json b/v3/internal/generator/testcases/complex_expressions/bound_types.json new file mode 100644 index 000000000..b00766ba0 --- /dev/null +++ b/v3/internal/generator/testcases/complex_expressions/bound_types.json @@ -0,0 +1,14 @@ +[ + ".Service1", + ".Service2", + ".Service3", + ".Service4", + ".Service5", + ".Service6", + "/config.Service7", + "/config.Service8", + "/config.Service9", + "/config.Service10", + "/config.Service11", + "/config.Service12" +] diff --git a/v3/internal/generator/testcases/complex_expressions/config/config.go b/v3/internal/generator/testcases/complex_expressions/config/config.go new file mode 100644 index 000000000..f5f40dd86 --- /dev/null +++ b/v3/internal/generator/testcases/complex_expressions/config/config.go @@ -0,0 +1,82 @@ +package config + +import "github.com/wailsapp/wails/v3/pkg/application" + +type Service7 struct{} +type Service8 struct{} +type Service9 struct{} +type Service10 struct{} +type Service11 struct{} +type Service12 struct{} + +func (*Service7) TestMethod() {} + +func (*Service9) TestMethod2() {} + +func NewService7() application.Service { + return application.NewService(new(Service7)) +} + +func NewService8() (result application.Service) { + result = application.NewService(&Service8{}) + return +} + +type ServiceProvider struct { + AService application.Service + *ProviderWithMethod + HeresAnotherOne application.Service +} + +type ProviderWithMethod struct { + OtherService any +} + +func (pm *ProviderWithMethod) Init() { + pm.OtherService = application.NewService(&Service10{}) +} + +var Services []application.Service + +func init() { + var ourServices = []application.Service{ + NewService7(), + NewService8(), + } + + Services = make([]application.Service, len(ourServices)) + + for i, el := range ourServices { + Services[len(ourServices)-i] = el + } +} + +func MoreServices() ServiceProvider { + var provider ServiceProvider + + provider.AService = application.NewService(&Service9{}) + provider.ProviderWithMethod = new(ProviderWithMethod) + + return provider +} + +type ProviderInitialiser interface { + InitProvider(provider any) +} + +type internalProviderInitialiser struct{} + +func NewProviderInitialiser() ProviderInitialiser { + return internalProviderInitialiser{} +} + +func (internalProviderInitialiser) InitProvider(provider any) { + switch p := provider.(type) { + case *ServiceProvider: + p.HeresAnotherOne = application.NewService(&Service11{}) + default: + if anyp, ok := p.(*any); ok { + *anyp = application.NewService(&Service12{}) + } + } +} diff --git a/v3/internal/generator/testcases/complex_expressions/main.go b/v3/internal/generator/testcases/complex_expressions/main.go new file mode 100644 index 000000000..eca22794e --- /dev/null +++ b/v3/internal/generator/testcases/complex_expressions/main.go @@ -0,0 +1,66 @@ +package main + +import ( + _ "embed" + "log" + "slices" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config" + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Service1 struct{} +type Service2 struct{} +type Service3 struct{} +type Service4 struct{} +type Service5 struct{} +type Service6 struct{} + +var GlobalServices []application.Service + +func main() { + services := []application.Service{ + application.NewService(&Service1{}), + } + + services = append(services, application.NewService(&Service2{}), application.Service{}, application.Service{}) + services[2] = application.NewService(&Service3{}) + + var options = application.Options{ + Services: config.Services, + } + + provider := config.MoreServices() + provider.Init() + + pinit := config.NewProviderInitialiser() + pinit.InitProvider(&provider) + // Method resolution should work here just like above. + config.NewProviderInitialiser().InitProvider(&services[3]) + + copy(options.Services, []application.Service{application.NewService(&Service4{}), provider.HeresAnotherOne, provider.OtherService.(application.Service)}) + (options.Services) = append(options.Services, slices.Insert(services, 1, GlobalServices[2:]...)...) + + app := application.New(options) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} + +func init() { + var global = make([]application.Service, 4) + + global[0] = application.NewService(&Service5{}) + global = slices.Replace(global, 1, 4, + application.NewService(&Service6{}), + config.MoreServices().AService, + ) + + GlobalServices = slices.Clip(global) +} diff --git a/v3/internal/generator/testcases/complex_instantiations/bound_types.json b/v3/internal/generator/testcases/complex_instantiations/bound_types.json new file mode 100644 index 000000000..663519c0c --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/bound_types.json @@ -0,0 +1,18 @@ +[ + ".Service1", + ".Service2", + ".Service3", + ".Service4", + ".Service5", + ".Service6", + ".Service7", + ".Service8", + ".Service9", + ".Service10", + ".Service11", + ".Service12", + ".Service13", + ".Service14", + ".Service15", + "/other.Service16" +] diff --git a/v3/internal/generator/testcases/complex_instantiations/factory.go b/v3/internal/generator/testcases/complex_instantiations/factory.go new file mode 100644 index 000000000..854af6967 --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/factory.go @@ -0,0 +1,21 @@ +package main + +import "github.com/wailsapp/wails/v3/pkg/application" + +type Factory[T any, U any] struct { + simpleFactory[T] +} + +func NewFactory[T any, U any]() *Factory[T, U] { + return &Factory[T, U]{} +} + +func (*Factory[T, U]) GetU() application.Service { + return application.NewService(new(U)) +} + +type simpleFactory[T any] struct{} + +func (simpleFactory[U]) Get() application.Service { + return application.NewService(new(U)) +} diff --git a/v3/internal/generator/testcases/complex_instantiations/funcs.go b/v3/internal/generator/testcases/complex_instantiations/funcs.go new file mode 100644 index 000000000..7aea0e7e0 --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/funcs.go @@ -0,0 +1,14 @@ +package main + +import "github.com/wailsapp/wails/v3/pkg/application" + +func ServiceInitialiser[T any]() func(*T) application.Service { + return application.NewService[T] +} + +func CustomNewServices[T any, U any]() []application.Service { + return []application.Service{ + application.NewService(new(T)), + application.NewService(new(U)), + } +} diff --git a/v3/internal/generator/testcases/complex_instantiations/main.go b/v3/internal/generator/testcases/complex_instantiations/main.go new file mode 100644 index 000000000..3b6a0fb6c --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/main.go @@ -0,0 +1,59 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/complex_instantiations/other" + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Service1 struct{} +type Service2 struct{} +type Service3 struct{} +type Service4 struct{} +type Service5 struct{} +type Service6 struct{} +type Service7 struct{} +type Service8 struct{} +type Service9 struct{} +type Service10 struct{} +type Service11 struct{} +type Service12 struct{} +type Service13 struct{} +type Service14 struct{} +type Service15 struct{} + +type SimplifiedFactory[T any] = Factory[T, Service15] + +func main() { + factory := NewFactory[Service1, Service2]() + otherFactory := other.NewFactory[Service3, Service4]() + + app := application.New(application.Options{ + Services: append(append( + []application.Service{ + factory.Get(), + factory.GetU(), + otherFactory.Get(), + otherFactory.GetU(), + application.NewService(&Service5{}), + ServiceInitialiser[Service6]()(&Service6{}), + other.CustomNewService(Service7{}), + other.ServiceInitialiser[Service8]()(&Service8{}), + application.NewServiceWithOptions(&Service13{}, application.ServiceOptions{Name: "custom name"}), + SimplifiedFactory[Service14]{}.Get(), + other.LocalService, + }, + CustomNewServices[Service9, Service10]()...), + other.CustomNewServices[Service11, Service12]()...), + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/internal/generator/testcases/complex_instantiations/other/factory.go b/v3/internal/generator/testcases/complex_instantiations/other/factory.go new file mode 100644 index 000000000..d8e3aadb7 --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/other/factory.go @@ -0,0 +1,21 @@ +package other + +import "github.com/wailsapp/wails/v3/pkg/application" + +type Factory[T any, U any] struct { + simpleFactory[T] +} + +func NewFactory[T any, U any]() *Factory[T, U] { + return &Factory[T, U]{} +} + +func (*Factory[T, U]) GetU() application.Service { + return application.NewService(new(U)) +} + +type simpleFactory[T any] struct{} + +func (simpleFactory[U]) Get() application.Service { + return application.NewService(new(U)) +} diff --git a/v3/internal/generator/testcases/complex_instantiations/other/funcs.go b/v3/internal/generator/testcases/complex_instantiations/other/funcs.go new file mode 100644 index 000000000..daf1ad066 --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/other/funcs.go @@ -0,0 +1,18 @@ +package other + +import "github.com/wailsapp/wails/v3/pkg/application" + +func CustomNewService[T any](srv T) application.Service { + return application.NewService(&srv) +} + +func ServiceInitialiser[T any]() func(*T) application.Service { + return application.NewService[T] +} + +func CustomNewServices[T any, U any]() []application.Service { + return []application.Service{ + application.NewService(new(T)), + application.NewService(new(U)), + } +} diff --git a/v3/internal/generator/testcases/complex_instantiations/other/local.go b/v3/internal/generator/testcases/complex_instantiations/other/local.go new file mode 100644 index 000000000..f08abb26a --- /dev/null +++ b/v3/internal/generator/testcases/complex_instantiations/other/local.go @@ -0,0 +1,7 @@ +package other + +import "github.com/wailsapp/wails/v3/pkg/application" + +type Service16 int + +var LocalService = application.NewService(new(Service16)) diff --git a/v3/internal/generator/testcases/complex_json/bound_types.json b/v3/internal/generator/testcases/complex_json/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/complex_json/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/complex_json/main.go b/v3/internal/generator/testcases/complex_json/main.go new file mode 100644 index 000000000..87e57912a --- /dev/null +++ b/v3/internal/generator/testcases/complex_json/main.go @@ -0,0 +1,124 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// Title is a title +type Title string + +const ( + // Mister is a title + Mister Title = "Mr" + Miss Title = "Miss" + Ms Title = "Ms" + Mrs Title = "Mrs" + Dr Title = "Dr" +) + +// GreetService is great +type GreetService struct{} + +type Embedded1 struct { + // Friends should be shadowed in Person by a field of lesser depth + Friends int + + // Vanish should be omitted from Person because there is another field with same depth and no tag + Vanish float32 + + // StillThere should be shadowed in Person by other field with same depth and a json tag + StillThere string + + // embedded4 should effectively appear as an embedded field + embedded4 + + // unexported should be invisible + unexported bool +} + +type Embedded2 struct { + // Vanish should be omitted from Person because there is another field with same depth and no tag + Vanish bool + + // StillThereButRenamed should shadow in Person the other field with same depth and no json tag + StillThereButRenamed *Embedded3 `json:"StillThere"` +} + +type Embedded3 string + +// Person represents a person +type Person struct { + // Titles is optional in JSON + Titles []Title `json:",omitzero"` + + // Names has a + // multiline comment + Names []string + + // Partner has a custom and complex JSON key + Partner *Person `json:"the person's partner ❤️"` + Friends []*Person + + Embedded1 + Embedded2 + + // UselessMap is invisible to JSON + UselessMap map[int]bool `json:"-"` + + // StrangeNumber maps to "-" + StrangeNumber float32 `json:"-,"` + + // Embedded3 should appear with key "Embedded3" + Embedded3 + + // StrangerNumber is serialized as a string + StrangerNumber int `json:",string"` + // StrangestString is optional and serialized as a JSON string + StrangestString string `json:",omitempty,string"` + // StringStrangest is serialized as a JSON string and optional + StringStrangest string `json:",string,omitempty"` + + // unexportedToo should be invisible even with a json tag + unexportedToo bool `json:"Unexported"` + + // embedded4 should be optional and appear with key "emb4" + embedded4 `json:"emb4,omitempty"` +} + +type embedded4 struct { + // NamingThingsIsHard is a law of programming + NamingThingsIsHard bool `json:",string"` + + // Friends should not be shadowed in Person as embedded4 is not embedded + // from encoding/json's point of view; + // however, it should be shadowed in Embedded1 + Friends bool + + // Embedded string should be invisible because it's unexported + string +} + +// Greet does XYZ +func (*GreetService) Greet(person Person, emb Embedded1) string { + return "" +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/complex_method/bound_types.json b/v3/internal/generator/testcases/complex_method/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/complex_method/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/complex_method/main.go b/v3/internal/generator/testcases/complex_method/main.go new file mode 100644 index 000000000..23ea29dee --- /dev/null +++ b/v3/internal/generator/testcases/complex_method/main.go @@ -0,0 +1,43 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct{} + +// Person represents a person +type Person struct { + Name string +} + +// Greet does XYZ +// It has a multiline doc comment +// The comment has even some */ traps!! +func (*GreetService) Greet(str string, people []Person, _ struct { + AnotherCount int + AnotherOne *Person +}, assoc map[int]*bool, _ []*float32, other ...string) (person Person, _ any, err1 error, _ []int, err error) { + return +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/cyclic_imports/bound_types.json b/v3/internal/generator/testcases/cyclic_imports/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/cyclic_imports/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/cyclic_imports/main.go b/v3/internal/generator/testcases/cyclic_imports/main.go new file mode 100644 index 000000000..58feb98a1 --- /dev/null +++ b/v3/internal/generator/testcases/cyclic_imports/main.go @@ -0,0 +1,55 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService int + +type StructA struct { + B *structB +} + +type structB struct { + A *StructA +} + +type StructC struct { + D structD +} + +type structD struct { + E StructE +} + +type StructE struct{} + +// Make a cycle. +func (GreetService) MakeCycles() (_ StructA, _ StructC) { + return +} + +func NewGreetService() application.Service { + return application.NewService(new(GreetService)) +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/cyclic_types/bound_types.json b/v3/internal/generator/testcases/cyclic_types/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/cyclic_types/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/cyclic_types/main.go b/v3/internal/generator/testcases/cyclic_types/main.go new file mode 100644 index 000000000..a5a9f5b46 --- /dev/null +++ b/v3/internal/generator/testcases/cyclic_types/main.go @@ -0,0 +1,46 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService int + +type Cyclic []map[string]Alias + +type Alias = *Cyclic + +type GenericCyclic[T any] []struct { + X *GenericCyclic[T] + Y []T +} + +// Make a cycle. +func (GreetService) MakeCycles() (_ Cyclic, _ GenericCyclic[GenericCyclic[int]]) { + return +} + +func NewGreetService() application.Service { + return application.NewService(new(GreetService)) +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/directives/bound_types.json b/v3/internal/generator/testcases/directives/bound_types.json new file mode 100644 index 000000000..62c02c0ca --- /dev/null +++ b/v3/internal/generator/testcases/directives/bound_types.json @@ -0,0 +1,5 @@ +[ + ".InternalService", + ".Service", + ".unexportedService" +] diff --git a/v3/internal/generator/testcases/directives/includes.go b/v3/internal/generator/testcases/directives/includes.go new file mode 100644 index 000000000..669d5305c --- /dev/null +++ b/v3/internal/generator/testcases/directives/includes.go @@ -0,0 +1,11 @@ +//wails:include js/test.js +//wails:include **:js/test_all.js +//wails:include *c:js/test_c.js +//wails:include *i:js/test_i.js +//wails:include j*:js/test_j.js +//wails:include jc:js/test_jc.js +//wails:include ji:js/test_ji.js +//wails:include t*:js/test_t.ts +//wails:include tc:js/test_tc.ts +//wails:include ti:js/test_ti.ts +package main diff --git a/v3/internal/generator/testcases/directives/internal.go b/v3/internal/generator/testcases/directives/internal.go new file mode 100644 index 000000000..86f06e02d --- /dev/null +++ b/v3/internal/generator/testcases/directives/internal.go @@ -0,0 +1,15 @@ +package main + +// An exported but internal model. +// +//wails:internal +type InternalModel struct { + Field string +} + +// An exported but internal service. +// +//wails:internal +type InternalService struct{} + +func (InternalService) Method(InternalModel) {} diff --git a/v3/internal/generator/testcases/directives/js/test.js b/v3/internal/generator/testcases/directives/js/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testcases/directives/js/test_all.js b/v3/internal/generator/testcases/directives/js/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testcases/directives/js/test_c.js b/v3/internal/generator/testcases/directives/js/test_c.js new file mode 100644 index 000000000..724e79e12 --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_c.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Classes"); diff --git a/v3/internal/generator/testcases/directives/js/test_i.js b/v3/internal/generator/testcases/directives/js/test_i.js new file mode 100644 index 000000000..442f20472 --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_i.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Interfaces"); diff --git a/v3/internal/generator/testcases/directives/js/test_j.js b/v3/internal/generator/testcases/directives/js/test_j.js new file mode 100644 index 000000000..b2f9c5edb --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testcases/directives/js/test_jc.js b/v3/internal/generator/testcases/directives/js/test_jc.js new file mode 100644 index 000000000..ddf4920e5 --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_jc.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS Classes"); diff --git a/v3/internal/generator/testcases/directives/js/test_ji.js b/v3/internal/generator/testcases/directives/js/test_ji.js new file mode 100644 index 000000000..36e28f09b --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_ji.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS Interfaces"); diff --git a/v3/internal/generator/testcases/directives/js/test_t.ts b/v3/internal/generator/testcases/directives/js/test_t.ts new file mode 100644 index 000000000..253d3f2f6 --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testcases/directives/js/test_tc.ts b/v3/internal/generator/testcases/directives/js/test_tc.ts new file mode 100644 index 000000000..66b739d3a --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_tc.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS Classes"); diff --git a/v3/internal/generator/testcases/directives/js/test_ti.ts b/v3/internal/generator/testcases/directives/js/test_ti.ts new file mode 100644 index 000000000..7400e97aa --- /dev/null +++ b/v3/internal/generator/testcases/directives/js/test_ti.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS Interfaces"); diff --git a/v3/internal/generator/testcases/directives/main.go b/v3/internal/generator/testcases/directives/main.go new file mode 100644 index 000000000..ee204027a --- /dev/null +++ b/v3/internal/generator/testcases/directives/main.go @@ -0,0 +1,60 @@ +//wails:inject console.log("Hello everywhere!"); +//wails:inject **:console.log("Hello everywhere again!"); +//wails:inject *c:console.log("Hello Classes!"); +//wails:inject *i:console.log("Hello Interfaces!"); +//wails:inject j*:console.log("Hello JS!"); +//wails:inject jc:console.log("Hello JS Classes!"); +//wails:inject ji:console.log("Hello JS Interfaces!"); +//wails:inject t*:console.log("Hello TS!"); +//wails:inject tc:console.log("Hello TS Classes!"); +//wails:inject ti:console.log("Hello TS Interfaces!"); +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage" + "github.com/wailsapp/wails/v3/pkg/application" +) + +type IgnoredType struct { + Field int +} + +//wails:inject j*:/** +//wails:inject j*: * @param {string} arg +//wails:inject j*: * @returns {Promise} +//wails:inject j*: */ +//wails:inject j*:export async function CustomMethod(arg) { +//wails:inject t*:export async function CustomMethod(arg: string): Promise { +//wails:inject await InternalMethod("Hello " + arg + "!"); +//wails:inject } +type Service struct{} + +func (*Service) VisibleMethod(otherpackage.Dummy) {} + +//wails:ignore +func (*Service) IgnoredMethod(IgnoredType) {} + +//wails:internal +func (*Service) InternalMethod(string) {} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&Service{}), + application.NewService(&unexportedService{}), + application.NewService(&InternalService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/directives/otherpackage/dummy.go b/v3/internal/generator/testcases/directives/otherpackage/dummy.go new file mode 100644 index 000000000..e8f688f05 --- /dev/null +++ b/v3/internal/generator/testcases/directives/otherpackage/dummy.go @@ -0,0 +1,5 @@ +//wails:include jc:js/*_j*.js +//wails:include tc:js/*_t*.ts +package otherpackage + +type Dummy struct{} diff --git a/v3/internal/generator/testcases/directives/otherpackage/js/test_j.js b/v3/internal/generator/testcases/directives/otherpackage/js/test_j.js new file mode 100644 index 000000000..2166d33b6 --- /dev/null +++ b/v3/internal/generator/testcases/directives/otherpackage/js/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testcases/directives/otherpackage/js/test_jc.js b/v3/internal/generator/testcases/directives/otherpackage/js/test_jc.js new file mode 100644 index 000000000..338898726 --- /dev/null +++ b/v3/internal/generator/testcases/directives/otherpackage/js/test_jc.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("JS Classes"); diff --git a/v3/internal/generator/testcases/directives/otherpackage/js/test_t.ts b/v3/internal/generator/testcases/directives/otherpackage/js/test_t.ts new file mode 100644 index 000000000..6703820f1 --- /dev/null +++ b/v3/internal/generator/testcases/directives/otherpackage/js/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testcases/directives/otherpackage/js/test_tc.ts b/v3/internal/generator/testcases/directives/otherpackage/js/test_tc.ts new file mode 100644 index 000000000..15d2994e9 --- /dev/null +++ b/v3/internal/generator/testcases/directives/otherpackage/js/test_tc.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("TS Classes"); diff --git a/v3/internal/generator/testcases/directives/unexported.go b/v3/internal/generator/testcases/directives/unexported.go new file mode 100644 index 000000000..56708c5ae --- /dev/null +++ b/v3/internal/generator/testcases/directives/unexported.go @@ -0,0 +1,11 @@ +package main + +// An unexported model. +type unexportedModel struct { + Field string +} + +// An unexported service. +type unexportedService struct{} + +func (unexportedService) Method(unexportedModel) {} diff --git a/v3/internal/generator/testcases/embedded_interface/bound_types.json b/v3/internal/generator/testcases/embedded_interface/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/embedded_interface/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/embedded_interface/main.go b/v3/internal/generator/testcases/embedded_interface/main.go new file mode 100644 index 000000000..2f65d6519 --- /dev/null +++ b/v3/internal/generator/testcases/embedded_interface/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + AnInterface +} + +type AnInterface interface { + // Comment 1. + Method1() + + Method2() // Comment 2. + + // Comment 3a. + Method3() // Comment 3b. + + interface { + // Comment 4. + Method4() + } + + InterfaceAlias +} + +type InterfaceAlias = interface { + // Comment 5. + Method5() +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/enum/bound_types.json b/v3/internal/generator/testcases/enum/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/enum/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/enum/main.go b/v3/internal/generator/testcases/enum/main.go new file mode 100644 index 000000000..22603ce37 --- /dev/null +++ b/v3/internal/generator/testcases/enum/main.go @@ -0,0 +1,74 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// Title is a title +type Title string + +const ( + // Mister is a title + Mister Title = "Mr" + Miss Title = "Miss" + Ms Title = "Ms" + Mrs Title = "Mrs" + Dr Title = "Dr" +) + +// Age is an integer with some predefined values +type Age = int + +const ( + NewBorn Age = 0 + Teenager Age = 12 + YoungAdult Age = 18 + + // Oh no, some grey hair! + MiddleAged Age = 50 + Mathusalem Age = 1000 // Unbelievable! +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string + target *Person +} + +// Person represents a person +type Person struct { + Title Title + Name string + Age Age +} + +// Greet does XYZ +func (*GreetService) Greet(name string, title Title) string { + return "Hello " + string(title) + " " + name +} + +// NewPerson creates a new person +func (*GreetService) NewPerson(name string) *Person { + return &Person{Name: name} +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/enum_from_imported_package/bound_types.json b/v3/internal/generator/testcases/enum_from_imported_package/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/enum_from_imported_package/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/enum_from_imported_package/main.go b/v3/internal/generator/testcases/enum_from_imported_package/main.go new file mode 100644 index 000000000..f0348eb5c --- /dev/null +++ b/v3/internal/generator/testcases/enum_from_imported_package/main.go @@ -0,0 +1,38 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet does XYZ +func (*GreetService) Greet(name string, title services.Title) string { + return "Hello " + title.String() + " " + name +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/enum_from_imported_package/services/other.go b/v3/internal/generator/testcases/enum_from_imported_package/services/other.go new file mode 100644 index 000000000..5d15f239c --- /dev/null +++ b/v3/internal/generator/testcases/enum_from_imported_package/services/other.go @@ -0,0 +1,16 @@ +package services + +type Title string + +func (t Title) String() string { + return string(t) +} + +const ( + // Mister is a title + Mister Title = "Mr" + Miss Title = "Miss" + Ms Title = "Ms" + Mrs Title = "Mrs" + Dr Title = "Dr" +) diff --git a/v3/internal/generator/testcases/function_from_imported_package/bound_types.json b/v3/internal/generator/testcases/function_from_imported_package/bound_types.json new file mode 100644 index 000000000..86a0a8812 --- /dev/null +++ b/v3/internal/generator/testcases/function_from_imported_package/bound_types.json @@ -0,0 +1,4 @@ +[ + ".GreetService", + "/services.OtherService" +] diff --git a/v3/internal/generator/testcases/function_from_imported_package/main.go b/v3/internal/generator/testcases/function_from_imported_package/main.go new file mode 100644 index 000000000..03e5b209f --- /dev/null +++ b/v3/internal/generator/testcases/function_from_imported_package/main.go @@ -0,0 +1,51 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string + target *Person +} + +// Person is a person +type Person struct { + Name string + Address *services.Address +} + +// Greet does XYZ +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +// NewPerson creates a new person +func (*GreetService) NewPerson(name string) *Person { + return &Person{Name: name} +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + services.NewOtherService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/function_from_imported_package/services/other.go b/v3/internal/generator/testcases/function_from_imported_package/services/other.go new file mode 100644 index 000000000..4ac6e6efd --- /dev/null +++ b/v3/internal/generator/testcases/function_from_imported_package/services/other.go @@ -0,0 +1,28 @@ +package services + +import "github.com/wailsapp/wails/v3/pkg/application" + +// OtherService is a struct +// that does things +type OtherService struct { + t int +} + +type Address struct { + Street string + State string + Country string +} + +// Yay does this and that +func (o *OtherService) Yay() *Address { + return &Address{ + Street: "123 Pitt Street", + State: "New South Wales", + Country: "Australia", + } +} + +func NewOtherService() application.Service { + return application.NewService(&OtherService{}) +} diff --git a/v3/internal/generator/testcases/function_from_nested_imported_package/bound_types.json b/v3/internal/generator/testcases/function_from_nested_imported_package/bound_types.json new file mode 100644 index 000000000..7f9f0ea61 --- /dev/null +++ b/v3/internal/generator/testcases/function_from_nested_imported_package/bound_types.json @@ -0,0 +1,4 @@ +[ + ".GreetService", + "/services/other.OtherService" +] diff --git a/v3/internal/generator/testcases/function_from_nested_imported_package/main.go b/v3/internal/generator/testcases/function_from_nested_imported_package/main.go new file mode 100644 index 000000000..820fc4f83 --- /dev/null +++ b/v3/internal/generator/testcases/function_from_nested_imported_package/main.go @@ -0,0 +1,50 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string + target *Person +} + +type Person struct { + Name string + Address *other.Address +} + +// Greet does XYZ +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +// NewPerson creates a new person +func (*GreetService) NewPerson(name string) *Person { + return &Person{Name: name} +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + other.NewOtherService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/other.go b/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/other.go new file mode 100644 index 000000000..fa12f63ce --- /dev/null +++ b/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/other.go @@ -0,0 +1,28 @@ +package other + +import "github.com/wailsapp/wails/v3/pkg/application" + +// OtherService is a struct +// that does things +type OtherService struct { + t int +} + +type Address struct { + Street string + State string + Country string +} + +// Yay does this and that +func (o *OtherService) Yay() *Address { + return &Address{ + Street: "123 Pitt Street", + State: "New South Wales", + Country: "Australia", + } +} + +func NewOtherService() application.Service { + return application.NewService(&OtherService{}) +} diff --git a/v3/internal/generator/testcases/function_multiple_files/bound_types.json b/v3/internal/generator/testcases/function_multiple_files/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/function_multiple_files/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/function_multiple_files/greet.go b/v3/internal/generator/testcases/function_multiple_files/greet.go new file mode 100644 index 000000000..b0153f22f --- /dev/null +++ b/v3/internal/generator/testcases/function_multiple_files/greet.go @@ -0,0 +1,20 @@ +package main + +import ( + _ "embed" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type GreetService struct { + SomeVariable int + lowerCase string +} + +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +func NewGreetService() application.Service { + return application.NewService(&GreetService{}) +} diff --git a/v3/internal/generator/testcases/function_multiple_files/main.go b/v3/internal/generator/testcases/function_multiple_files/main.go new file mode 100644 index 000000000..240168530 --- /dev/null +++ b/v3/internal/generator/testcases/function_multiple_files/main.go @@ -0,0 +1,25 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/function_single/bound_types.json b/v3/internal/generator/testcases/function_single/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/function_single/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/function_single/main.go b/v3/internal/generator/testcases/function_single/main.go new file mode 100644 index 000000000..738505455 --- /dev/null +++ b/v3/internal/generator/testcases/function_single/main.go @@ -0,0 +1,40 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +func NewGreetService() application.Service { + return application.NewService(&GreetService{}) +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/function_single_context/bound_types.json b/v3/internal/generator/testcases/function_single_context/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/function_single_context/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/function_single_context/main.go b/v3/internal/generator/testcases/function_single_context/main.go new file mode 100644 index 000000000..1a785ddba --- /dev/null +++ b/v3/internal/generator/testcases/function_single_context/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +// Greet someone +func (*GreetService) GreetWithContext(ctx context.Context, name string) string { + return "Hello " + name +} + +func NewGreetService() application.Service { + return application.NewService(&GreetService{}) +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/function_single_internal/bound_types.json b/v3/internal/generator/testcases/function_single_internal/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/function_single_internal/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/function_single_internal/main.go b/v3/internal/generator/testcases/function_single_internal/main.go new file mode 100644 index 000000000..f66dd3649 --- /dev/null +++ b/v3/internal/generator/testcases/function_single_internal/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "context" + _ "embed" + "log" + "net/http" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +// Debugging name +func (*GreetService) ServiceName() string { + return "GreetService" +} + +// Lifecycle +func (*GreetService) ServiceStartup(context.Context, application.ServiceOptions) error { + return nil +} + +// Lifecycle +func (*GreetService) ServiceShutdown() error { + return nil +} + +// Serve some routes +func (*GreetService) ServeHTTP(http.ResponseWriter, *http.Request) { +} + +func NewGreetService() application.Service { + return application.NewService(&GreetService{}) +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + NewGreetService(), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/map_keys/bound_types.json b/v3/internal/generator/testcases/map_keys/bound_types.json new file mode 100644 index 000000000..1a27d889e --- /dev/null +++ b/v3/internal/generator/testcases/map_keys/bound_types.json @@ -0,0 +1,3 @@ +[ + ".Service" +] diff --git a/v3/internal/generator/testcases/map_keys/main.go b/v3/internal/generator/testcases/map_keys/main.go new file mode 100644 index 000000000..b5d331bad --- /dev/null +++ b/v3/internal/generator/testcases/map_keys/main.go @@ -0,0 +1,307 @@ +package main + +import ( + _ "embed" + "encoding" + "encoding/json" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Service struct{} + +type NonTextMarshaler struct{} + +type ValueTextMarshaler struct{} + +func (ValueTextMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +type PointerTextMarshaler struct{} + +func (*PointerTextMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +type JsonTextMarshaler struct{} + +func (JsonTextMarshaler) MarshalJSON() ([]byte, error) { return nil, nil } +func (JsonTextMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +type CustomInterface interface { + MarshalText() ([]byte, error) +} + +type EmbeddedInterface interface { + encoding.TextMarshaler +} + +type EmbeddedInterfaces interface { + json.Marshaler + encoding.TextMarshaler +} + +type BasicConstraint interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | + ~string +} + +type BadTildeConstraint interface { + int | ~struct{} | string +} + +type GoodTildeConstraint interface { + int | ~struct{} | string + MarshalText() ([]byte, error) +} + +type NonBasicConstraint interface { + ValueTextMarshaler | *PointerTextMarshaler +} + +type PointableConstraint interface { + ValueTextMarshaler | PointerTextMarshaler +} + +type MixedConstraint interface { + uint | ~string | ValueTextMarshaler | *PointerTextMarshaler +} + +type InterfaceConstraint interface { + comparable + encoding.TextMarshaler +} + +type PointerConstraint[T comparable] interface { + *T + encoding.TextMarshaler +} + +type EmbeddedValue struct{ ValueTextMarshaler } +type EmbeddedValuePtr struct{ *ValueTextMarshaler } +type EmbeddedPointer struct{ PointerTextMarshaler } +type EmbeddedPointerPtr struct{ *PointerTextMarshaler } + +type EmbeddedCustomInterface struct{ CustomInterface } +type EmbeddedOriginalInterface struct{ encoding.TextMarshaler } + +type WrongType bool +type WrongAlias = bool +type StringType string +type StringAlias = string +type IntType int +type IntAlias = int + +type ValueType ValueTextMarshaler +type ValuePtrType *ValueTextMarshaler +type ValueAlias = ValueTextMarshaler +type ValuePtrAlias = *ValueTextMarshaler + +type PointerType PointerTextMarshaler +type PointerPtrType *PointerTextMarshaler +type PointerAlias = PointerTextMarshaler +type PointerPtrAlias = *PointerTextMarshaler + +type InterfaceType encoding.TextMarshaler +type InterfacePtrType *encoding.TextMarshaler +type InterfaceAlias = encoding.TextMarshaler +type InterfacePtrAlias = *encoding.TextMarshaler + +type ComparableCstrAlias[R comparable] = R +type ComparableCstrPtrAlias[R comparable] = *R +type BasicCstrAlias[S BasicConstraint] = S +type BasicCstrPtrAlias[S BasicConstraint] = *S +type BadTildeCstrAlias[T BadTildeConstraint] = T +type BadTildeCstrPtrAlias[T BadTildeConstraint] = *T +type GoodTildeCstrAlias[U GoodTildeConstraint] = U +type GoodTildeCstrPtrAlias[U GoodTildeConstraint] = *U +type NonBasicCstrAlias[V NonBasicConstraint] = V +type NonBasicCstrPtrAlias[V NonBasicConstraint] = *V +type PointableCstrAlias[W PointableConstraint] = W +type PointableCstrPtrAlias[W PointableConstraint] = *W +type MixedCstrAlias[X MixedConstraint] = X +type MixedCstrPtrAlias[X MixedConstraint] = *X +type InterfaceCstrAlias[Y InterfaceConstraint] = Y +type InterfaceCstrPtrAlias[Y InterfaceConstraint] = *Y +type PointerCstrAlias[R comparable, Z PointerConstraint[R]] = Z +type PointerCstrPtrAlias[R comparable, Z PointerConstraint[R]] = *Z + +type Maps[R comparable, S BasicConstraint, T BadTildeConstraint, U GoodTildeConstraint, V NonBasicConstraint, W PointableConstraint, X MixedConstraint, Y InterfaceConstraint, Z PointerConstraint[R]] struct { + Bool map[bool]int // Reject + Int map[int]int // Accept + Uint map[uint]int // Accept + Float map[float32]int // Reject + Complex map[complex64]int // Reject + Byte map[byte]int // Accept + Rune map[rune]int // Accept + String map[string]int // Accept + + IntPtr map[*int]int // Reject + UintPtr map[*uint]int // Reject + FloatPtr map[*float32]int // Reject + ComplexPtr map[*complex64]int // Reject + StringPtr map[*string]int // Reject + + NTM map[NonTextMarshaler]int // Reject + NTMPtr map[*NonTextMarshaler]int // Reject + VTM map[ValueTextMarshaler]int // Accept + VTMPtr map[*ValueTextMarshaler]int // Accept + PTM map[PointerTextMarshaler]int // Reject + PTMPtr map[*PointerTextMarshaler]int // Accept + JTM map[JsonTextMarshaler]int // Accept, hide + JTMPtr map[*JsonTextMarshaler]int // Accept, hide + + A map[any]int // Reject + APtr map[*any]int // Reject + TM map[encoding.TextMarshaler]int // Accept, hide + TMPtr map[*encoding.TextMarshaler]int // Reject + CI map[CustomInterface]int // Accept, hide + CIPtr map[*CustomInterface]int // Reject + EI map[EmbeddedInterface]int // Accept, hide + EIPtr map[*EmbeddedInterface]int // Reject + + EV map[EmbeddedValue]int // Accept + EVPtr map[*EmbeddedValue]int // Accept + EVP map[EmbeddedValuePtr]int // Accept + EVPPtr map[*EmbeddedValuePtr]int // Accept + EP map[EmbeddedPointer]int // Reject + EPPtr map[*EmbeddedPointer]int // Accept + EPP map[EmbeddedPointerPtr]int // Accept + EPPPtr map[*EmbeddedPointerPtr]int // Accept + + ECI map[EmbeddedCustomInterface]int // Accept + ECIPtr map[*EmbeddedCustomInterface]int // Accept + EOI map[EmbeddedOriginalInterface]int // Accept + EOIPtr map[*EmbeddedOriginalInterface]int // Accept + + WT map[WrongType]int // Reject + WA map[WrongAlias]int // Reject + ST map[StringType]int // Accept + SA map[StringAlias]int // Accept + IntT map[IntType]int // Accept + IntA map[IntAlias]int // Accept + + VT map[ValueType]int // Reject + VTPtr map[*ValueType]int // Reject + VPT map[ValuePtrType]int // Reject + VPTPtr map[*ValuePtrType]int // Reject + VA map[ValueAlias]int // Accept + VAPtr map[*ValueAlias]int // Accept + VPA map[ValuePtrAlias]int // Accept, hide + VPAPtr map[*ValuePtrAlias]int // Reject + + PT map[PointerType]int // Reject + PTPtr map[*PointerType]int // Reject + PPT map[PointerPtrType]int // Reject + PPTPtr map[*PointerPtrType]int // Reject + PA map[PointerAlias]int // Reject + PAPtr map[*PointerAlias]int // Accept + PPA map[PointerPtrAlias]int // Accept, hide + PPAPtr map[*PointerPtrAlias]int // Reject + + IT map[InterfaceType]int // Accept, hide + ITPtr map[*InterfaceType]int // Reject + IPT map[InterfacePtrType]int // Reject + IPTPtr map[*InterfacePtrType]int // Reject + IA map[InterfaceAlias]int // Accept, hide + IAPtr map[*InterfaceAlias]int // Reject + IPA map[InterfacePtrAlias]int // Reject + IPAPtr map[*InterfacePtrAlias]int // Reject + + TPR map[R]int // Soft reject + TPRPtr map[*R]int // Soft reject + TPS map[S]int // Accept, hide + TPSPtr map[*S]int // Soft reject + TPT map[T]int // Soft reject + TPTPtr map[*T]int // Soft reject + TPU map[U]int // Accept, hide + TPUPtr map[*U]int // Soft reject + TPV map[V]int // Accept, hide + TPVPtr map[*V]int // Soft reject + TPW map[W]int // Soft reject + TPWPtr map[*W]int // Accept, hide + TPX map[X]int // Accept, hide + TPXPtr map[*X]int // Soft reject + TPY map[Y]int // Accept, hide + TPYPtr map[*Y]int // Soft reject + TPZ map[Z]int // Accept, hide + TPZPtr map[*Z]int // Soft reject + + GAR map[ComparableCstrAlias[R]]int // Soft reject + GARPtr map[ComparableCstrPtrAlias[R]]int // Soft reject + GAS map[BasicCstrAlias[S]]int // Accept, hide + GASPtr map[BasicCstrPtrAlias[S]]int // Soft reject + GAT map[BadTildeCstrAlias[T]]int // Soft reject + GATPtr map[BadTildeCstrPtrAlias[T]]int // Soft reject + GAU map[GoodTildeCstrAlias[U]]int // Accept, hide + GAUPtr map[GoodTildeCstrPtrAlias[U]]int // Soft reject + GAV map[NonBasicCstrAlias[V]]int // Accept, hide + GAVPtr map[NonBasicCstrPtrAlias[V]]int // Soft reject + GAW map[PointableCstrAlias[W]]int // Soft reject + GAWPtr map[PointableCstrPtrAlias[W]]int // Accept, hide + GAX map[MixedCstrAlias[X]]int // Accept, hide + GAXPtr map[MixedCstrPtrAlias[X]]int // Soft reject + GAY map[InterfaceCstrAlias[Y]]int // Accept, hide + GAYPtr map[InterfaceCstrPtrAlias[Y]]int // Soft reject + GAZ map[PointerCstrAlias[R, Z]]int // Accept, hide + GAZPtr map[PointerCstrPtrAlias[R, Z]]int // Soft reject + + GACi map[ComparableCstrAlias[int]]int // Accept, hide + GACV map[ComparableCstrAlias[ValueTextMarshaler]]int // Accept + GACP map[ComparableCstrAlias[PointerTextMarshaler]]int // Reject + GACiPtr map[ComparableCstrPtrAlias[int]]int // Reject + GACVPtr map[ComparableCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide + GACPPtr map[ComparableCstrPtrAlias[PointerTextMarshaler]]int // Accept, hide + GABi map[BasicCstrAlias[int]]int // Accept, hide + GABs map[BasicCstrAlias[string]]int // Accept + GABiPtr map[BasicCstrPtrAlias[int]]int // Reject + GABT map[BadTildeCstrAlias[struct{}]]int // Reject + GABTPtr map[BadTildeCstrPtrAlias[struct{}]]int // Reject + GAGT map[GoodTildeCstrAlias[ValueTextMarshaler]]int // Accept + GAGTPtr map[GoodTildeCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide + GANBV map[NonBasicCstrAlias[ValueTextMarshaler]]int // Accept + GANBP map[NonBasicCstrAlias[*PointerTextMarshaler]]int // Accept, hide + GANBVPtr map[NonBasicCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide + GANBPPtr map[NonBasicCstrPtrAlias[*PointerTextMarshaler]]int // Reject + GAPlV1 map[PointableCstrAlias[ValueTextMarshaler]]int // Accept + GAPlV2 map[*PointableCstrAlias[ValueTextMarshaler]]int // Accept + GAPlP1 map[PointableCstrAlias[PointerTextMarshaler]]int // Reject + GAPlP2 map[*PointableCstrAlias[PointerTextMarshaler]]int // Accept + GAPlVPtr map[PointableCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide + GAPlPPtr map[PointableCstrPtrAlias[PointerTextMarshaler]]int // Accept, hide + GAMi map[MixedCstrAlias[uint]]int // Accept, hide + GAMS map[MixedCstrAlias[StringType]]int // Accept + GAMV map[MixedCstrAlias[ValueTextMarshaler]]int // Accept + GAMSPtr map[MixedCstrPtrAlias[StringType]]int // Reject + GAMVPtr map[MixedCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide + GAII map[InterfaceCstrAlias[encoding.TextMarshaler]]int // Accept, hide + GAIV map[InterfaceCstrAlias[ValueTextMarshaler]]int // Accept + GAIP map[InterfaceCstrAlias[*PointerTextMarshaler]]int // Accept, hide + GAIIPtr map[InterfaceCstrPtrAlias[encoding.TextMarshaler]]int // Reject + GAIVPtr map[InterfaceCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide + GAIPPtr map[InterfaceCstrPtrAlias[*PointerTextMarshaler]]int // Reject + GAPrV map[PointerCstrAlias[ValueTextMarshaler, *ValueTextMarshaler]]int // Accept, hide + GAPrP map[PointerCstrAlias[PointerTextMarshaler, *PointerTextMarshaler]]int // Accept, hide + GAPrVPtr map[PointerCstrPtrAlias[ValueTextMarshaler, *ValueTextMarshaler]]int // Reject + GAPrPPtr map[PointerCstrPtrAlias[PointerTextMarshaler, *PointerTextMarshaler]]int // Reject +} + +func (*Service) Method() (_ Maps[PointerTextMarshaler, int, int, ValueTextMarshaler, *PointerTextMarshaler, ValueTextMarshaler, StringType, ValueTextMarshaler, *PointerTextMarshaler]) { + return +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&Service{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/marshalers/bound_types.json b/v3/internal/generator/testcases/marshalers/bound_types.json new file mode 100644 index 000000000..1a27d889e --- /dev/null +++ b/v3/internal/generator/testcases/marshalers/bound_types.json @@ -0,0 +1,3 @@ +[ + ".Service" +] diff --git a/v3/internal/generator/testcases/marshalers/main.go b/v3/internal/generator/testcases/marshalers/main.go new file mode 100644 index 000000000..32c934e49 --- /dev/null +++ b/v3/internal/generator/testcases/marshalers/main.go @@ -0,0 +1,209 @@ +package main + +import ( + _ "embed" + "encoding" + "encoding/json" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Service struct{} + +// class {} +type NonMarshaler struct{} + +// any +type ValueJsonMarshaler struct{} + +func (ValueJsonMarshaler) MarshalJSON() ([]byte, error) { return nil, nil } + +// any +type PointerJsonMarshaler struct{} + +func (*PointerJsonMarshaler) MarshalJSON() ([]byte, error) { return nil, nil } + +// string +type ValueTextMarshaler struct{} + +func (ValueTextMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +// string +type PointerTextMarshaler struct{} + +func (*PointerTextMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +// any +type ValueMarshaler struct{} + +func (ValueMarshaler) MarshalJSON() ([]byte, error) { return nil, nil } +func (ValueMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +// any +type PointerMarshaler struct{} + +func (*PointerMarshaler) MarshalJSON() ([]byte, error) { return nil, nil } +func (*PointerMarshaler) MarshalText() ([]byte, error) { return nil, nil } + +// any +type UnderlyingJsonMarshaler struct{ json.Marshaler } + +// string +type UnderlyingTextMarshaler struct{ encoding.TextMarshaler } + +// any +type UnderlyingMarshaler struct { + json.Marshaler + encoding.TextMarshaler +} + +type customJsonMarshaler interface { + MarshalJSON() ([]byte, error) +} + +type customTextMarshaler interface { + MarshalText() ([]byte, error) +} + +type customMarshaler interface { + MarshalJSON() ([]byte, error) + MarshalText() ([]byte, error) +} + +// struct{} +type AliasNonMarshaler = struct{} + +// any +type AliasJsonMarshaler = struct{ json.Marshaler } + +// string +type AliasTextMarshaler = struct{ encoding.TextMarshaler } + +// any +type AliasMarshaler = struct { + json.Marshaler + encoding.TextMarshaler +} + +// any +type ImplicitJsonMarshaler UnderlyingJsonMarshaler + +// string +type ImplicitTextMarshaler UnderlyingTextMarshaler + +// any +type ImplicitMarshaler UnderlyingMarshaler + +// string +type ImplicitNonJson UnderlyingMarshaler + +func (ImplicitNonJson) MarshalJSON() {} + +// any +type ImplicitNonText UnderlyingMarshaler + +func (ImplicitNonText) MarshalText() {} + +// class{ Marshaler, TextMarshaler } +type ImplicitNonMarshaler UnderlyingMarshaler + +func (ImplicitNonMarshaler) MarshalJSON() {} +func (ImplicitNonMarshaler) MarshalText() {} + +// any +type ImplicitJsonButText UnderlyingJsonMarshaler + +func (ImplicitJsonButText) MarshalText() ([]byte, error) { return nil, nil } + +// any +type ImplicitTextButJson UnderlyingTextMarshaler + +func (ImplicitTextButJson) MarshalJSON() ([]byte, error) { return nil, nil } + +type Data struct { + NM NonMarshaler + NMPtr *NonMarshaler // NonMarshaler | null + + VJM ValueJsonMarshaler + VJMPtr *ValueJsonMarshaler // ValueJsonMarshaler | null + PJM PointerJsonMarshaler + PJMPtr *PointerJsonMarshaler // PointerJsonMarshaler | null + + VTM ValueTextMarshaler + VTMPtr *ValueTextMarshaler // ValueTextMarshaler | null + PTM PointerTextMarshaler + PTMPtr *PointerTextMarshaler // PointerTextMarshaler | null + + VM ValueMarshaler + VMPtr *ValueMarshaler // ValueMarshaler | null + PM PointerMarshaler + PMPtr *PointerMarshaler // PointerMarshaler | null + + UJM UnderlyingJsonMarshaler + UJMPtr *UnderlyingJsonMarshaler // UnderlyingJsonMarshaler | null + UTM UnderlyingTextMarshaler + UTMPtr *UnderlyingTextMarshaler // UnderlyingTextMarshaler | null + UM UnderlyingMarshaler + UMPtr *UnderlyingMarshaler // UnderlyingMarshaler | null + + JM struct{ json.Marshaler } // any + JMPtr *struct{ json.Marshaler } // any | null + TM struct{ encoding.TextMarshaler } // string + TMPtr *struct{ encoding.TextMarshaler } // string | null + CJM struct{ customJsonMarshaler } // any + CJMPtr *struct{ customJsonMarshaler } // any | null + CTM struct{ customTextMarshaler } // string + CTMPtr *struct{ customTextMarshaler } // string | null + CM struct{ customMarshaler } // any + CMPtr *struct{ customMarshaler } // any | null + + ANM AliasNonMarshaler + ANMPtr *AliasNonMarshaler // AliasNonMarshaler | null + AJM AliasJsonMarshaler + AJMPtr *AliasJsonMarshaler // AliasJsonMarshaler | null + ATM AliasTextMarshaler + ATMPtr *AliasTextMarshaler // AliasTextMarshaler | null + AM AliasMarshaler + AMPtr *AliasMarshaler // AliasMarshaler | null + + ImJM ImplicitJsonMarshaler + ImJMPtr *ImplicitJsonMarshaler // ImplicitJsonMarshaler | null + ImTM ImplicitTextMarshaler + ImTMPtr *ImplicitTextMarshaler // ImplicitTextMarshaler | null + ImM ImplicitMarshaler + ImMPtr *ImplicitMarshaler // ImplicitMarshaler | null + + ImNJ ImplicitNonJson + ImNJPtr *ImplicitNonJson // ImplicitNonJson | null + ImNT ImplicitNonText + ImNTPtr *ImplicitNonText // ImplicitNonText | null + ImNM ImplicitNonMarshaler + ImNMPtr *ImplicitNonMarshaler // ImplicitNonMarshaler | null + + ImJbT ImplicitJsonButText + ImJbTPtr *ImplicitJsonButText // ImplicitJsonButText | null + ImTbJ ImplicitTextButJson + ImTbJPtr *ImplicitTextButJson // ImplicitTextButJson | null +} + +func (*Service) Method() (_ Data) { + return +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&Service{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/no_bindings_here/other/othermethods.go b/v3/internal/generator/testcases/no_bindings_here/other/othermethods.go new file mode 100644 index 000000000..8d4ec6ba8 --- /dev/null +++ b/v3/internal/generator/testcases/no_bindings_here/other/othermethods.go @@ -0,0 +1,11 @@ +package other + +// OtherMethods has another method, but through a private embedded type. +type OtherMethods struct { + otherMethodsImpl +} + +type otherMethodsImpl int + +// LikeThisOtherOne does nothing as well, but is different. +func (*otherMethodsImpl) LikeThisOtherOne() {} diff --git a/v3/internal/generator/testcases/no_bindings_here/other/otherperson.go b/v3/internal/generator/testcases/no_bindings_here/other/otherperson.go new file mode 100644 index 000000000..516c965b5 --- /dev/null +++ b/v3/internal/generator/testcases/no_bindings_here/other/otherperson.go @@ -0,0 +1,10 @@ +package other + +// OtherPerson is like a person, but different. +type OtherPerson[T any] struct { + // They have a name as well. + Name string + + // But they may have many differences. + Differences []T +} diff --git a/v3/internal/generator/testcases/no_bindings_here/person.go b/v3/internal/generator/testcases/no_bindings_here/person.go new file mode 100644 index 000000000..b0b6f941d --- /dev/null +++ b/v3/internal/generator/testcases/no_bindings_here/person.go @@ -0,0 +1,26 @@ +package nobindingshere + +import "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other" + +// Person is not a number. +type Person struct { + // They have a name. + Name string + Friends [4]Impersonator // Exactly 4 sketchy friends. +} + +// Impersonator gets their fields from other people. +type Impersonator = other.OtherPerson[int] + +// HowDifferent is a curious kind of person +// that lets other people decide how they are different. +type HowDifferent[How any] other.OtherPerson[map[string]How] + +// PrivatePerson gets their fields from hidden sources. +type PrivatePerson = personImpl + +type personImpl struct { + // Nickname conceals a person's identity. + Nickname string + Person +} diff --git a/v3/internal/generator/testcases/no_bindings_here/somemethods.go b/v3/internal/generator/testcases/no_bindings_here/somemethods.go new file mode 100644 index 000000000..4b0602a64 --- /dev/null +++ b/v3/internal/generator/testcases/no_bindings_here/somemethods.go @@ -0,0 +1,13 @@ +package nobindingshere + +import "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other" + +// SomeMethods exports some methods. +type SomeMethods struct { + other.OtherMethods +} + +// LikeThisOne is an example method that does nothing. +func (SomeMethods) LikeThisOne() (_ Person, _ HowDifferent[bool], _ PrivatePerson) { + return +} diff --git a/v3/internal/generator/testcases/out_of_tree/bound_types.json b/v3/internal/generator/testcases/out_of_tree/bound_types.json new file mode 100644 index 000000000..c0714f177 --- /dev/null +++ b/v3/internal/generator/testcases/out_of_tree/bound_types.json @@ -0,0 +1,7 @@ +[ + ".GreetService", + ".EmbedService", + ".EmbedOther", + "/../no_bindings_here.SomeMethods", + "/../no_bindings_here/other.OtherMethods" +] diff --git a/v3/internal/generator/testcases/out_of_tree/main.go b/v3/internal/generator/testcases/out_of_tree/main.go new file mode 100644 index 000000000..c3dfbc9ee --- /dev/null +++ b/v3/internal/generator/testcases/out_of_tree/main.go @@ -0,0 +1,47 @@ +package main + +import ( + _ "embed" + "log" + + nobindingshere "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here" + "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other" + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService int + +// EmbedService is tricky. +type EmbedService struct { + nobindingshere.SomeMethods +} + +// EmbedOther is even trickier. +type EmbedOther struct { + other.OtherMethods +} + +// Greet someone +func (*GreetService) Greet(string) {} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(new(GreetService)), + application.NewService(&EmbedService{}), + application.NewService(&EmbedOther{}), + application.NewService(&nobindingshere.SomeMethods{}), + application.NewService(&other.OtherMethods{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/struct_literal_multiple/bound_types.json b/v3/internal/generator/testcases/struct_literal_multiple/bound_types.json new file mode 100644 index 000000000..be815a97e --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple/bound_types.json @@ -0,0 +1,4 @@ +[ + ".GreetService", + ".OtherService" +] diff --git a/v3/internal/generator/testcases/struct_literal_multiple/main.go b/v3/internal/generator/testcases/struct_literal_multiple/main.go new file mode 100644 index 000000000..5e1081504 --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple/main.go @@ -0,0 +1,41 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type GreetService struct { + SomeVariable int + lowerCase string +} + +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +type OtherService struct { + t int +} + +func (o *OtherService) Hello() {} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + application.NewService(&OtherService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/struct_literal_multiple_files/bound_types.json b/v3/internal/generator/testcases/struct_literal_multiple_files/bound_types.json new file mode 100644 index 000000000..be815a97e --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_files/bound_types.json @@ -0,0 +1,4 @@ +[ + ".GreetService", + ".OtherService" +] diff --git a/v3/internal/generator/testcases/struct_literal_multiple_files/greet.go b/v3/internal/generator/testcases/struct_literal_multiple_files/greet.go new file mode 100644 index 000000000..2a45396a7 --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_files/greet.go @@ -0,0 +1,14 @@ +package main + +import ( + _ "embed" +) + +type GreetService struct { + SomeVariable int + lowerCase string +} + +func (*GreetService) Greet(name string) string { + return "Hello " + name +} diff --git a/v3/internal/generator/testcases/struct_literal_multiple_files/main.go b/v3/internal/generator/testcases/struct_literal_multiple_files/main.go new file mode 100644 index 000000000..ae80a4cba --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_files/main.go @@ -0,0 +1,26 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + application.NewService(&OtherService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/struct_literal_multiple_files/other.go b/v3/internal/generator/testcases/struct_literal_multiple_files/other.go new file mode 100644 index 000000000..ad5e661ef --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_files/other.go @@ -0,0 +1,7 @@ +package main + +type OtherService struct { + t int +} + +func (o *OtherService) Hello() {} diff --git a/v3/internal/generator/testcases/struct_literal_multiple_other/bound_types.json b/v3/internal/generator/testcases/struct_literal_multiple_other/bound_types.json new file mode 100644 index 000000000..86a0a8812 --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_other/bound_types.json @@ -0,0 +1,4 @@ +[ + ".GreetService", + "/services.OtherService" +] diff --git a/v3/internal/generator/testcases/struct_literal_multiple_other/main.go b/v3/internal/generator/testcases/struct_literal_multiple_other/main.go new file mode 100644 index 000000000..7b7a3a2ab --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_other/main.go @@ -0,0 +1,49 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services" + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string + target *Person +} + +type Person struct { + Name string + Address *services.Address +} + +// Greet does XYZ +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +// NewPerson creates a new person +func (*GreetService) NewPerson(name string) *Person { + return &Person{Name: name} +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + application.NewService(&services.OtherService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/struct_literal_multiple_other/services/other.go b/v3/internal/generator/testcases/struct_literal_multiple_other/services/other.go new file mode 100644 index 000000000..55472595b --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_multiple_other/services/other.go @@ -0,0 +1,22 @@ +package services + +// OtherService is a struct +// that does things +type OtherService struct { + t int +} + +type Address struct { + Street string + State string + Country string +} + +// Yay does this and that +func (o *OtherService) Yay() *Address { + return &Address{ + Street: "123 Pitt Street", + State: "New South Wales", + Country: "Australia", + } +} diff --git a/v3/internal/generator/testcases/struct_literal_non_pointer_single/bound_types.json b/v3/internal/generator/testcases/struct_literal_non_pointer_single/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_non_pointer_single/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/struct_literal_non_pointer_single/main.go b/v3/internal/generator/testcases/struct_literal_non_pointer_single/main.go new file mode 100644 index 000000000..e0fd8247d --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_non_pointer_single/main.go @@ -0,0 +1,205 @@ +package main + +import ( + _ "embed" + "log" + "strings" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Person struct { + Name string + Parent *Person + Details struct { + Age int + Address struct { + Street string + } + } +} + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (GreetService) Greet(name string) string { + return "Hello " + name +} + +func (GreetService) NoInputsStringOut() string { + return "Hello" +} + +func (GreetService) StringArrayInputStringOut(in []string) string { + return strings.Join(in, ",") +} + +func (GreetService) StringArrayInputStringArrayOut(in []string) []string { + return in +} + +func (GreetService) StringArrayInputNamedOutput(in []string) (output []string) { + return in +} + +func (GreetService) StringArrayInputNamedOutputs(in []string) (output []string, err error) { + return in, nil +} + +func (GreetService) IntPointerInputNamedOutputs(in *int) (output *int, err error) { + return in, nil +} + +func (GreetService) UIntPointerInAndOutput(in *uint) *uint { + return in +} + +func (GreetService) UInt8PointerInAndOutput(in *uint8) *uint8 { + return in +} + +func (GreetService) UInt16PointerInAndOutput(in *uint16) *uint16 { + return in +} + +func (GreetService) UInt32PointerInAndOutput(in *uint32) *uint32 { + return in +} + +func (GreetService) UInt64PointerInAndOutput(in *uint64) *uint64 { + return in +} + +func (GreetService) IntPointerInAndOutput(in *int) *int { + return in +} + +func (GreetService) Int8PointerInAndOutput(in *int8) *int8 { + return in +} + +func (GreetService) Int16PointerInAndOutput(in *int16) *int16 { + return in +} + +func (GreetService) Int32PointerInAndOutput(in *int32) *int32 { + return in +} + +func (GreetService) Int64PointerInAndOutput(in *int64) *int64 { + return in +} + +func (GreetService) IntInIntOut(in int) int { + return in +} + +func (GreetService) Int8InIntOut(in int8) int8 { + return in +} +func (GreetService) Int16InIntOut(in int16) int16 { + return in +} +func (GreetService) Int32InIntOut(in int32) int32 { + return in +} +func (GreetService) Int64InIntOut(in int64) int64 { + return in +} + +func (GreetService) UIntInUIntOut(in uint) uint { + return in +} + +func (GreetService) UInt8InUIntOut(in uint8) uint8 { + return in +} +func (GreetService) UInt16InUIntOut(in uint16) uint16 { + return in +} +func (GreetService) UInt32InUIntOut(in uint32) uint32 { + return in +} +func (GreetService) UInt64InUIntOut(in uint64) uint64 { + return in +} + +func (GreetService) Float32InFloat32Out(in float32) float32 { + return in +} + +func (GreetService) Float64InFloat64Out(in float64) float64 { + return in +} + +func (GreetService) PointerFloat32InFloat32Out(in *float32) *float32 { + return in +} + +func (GreetService) PointerFloat64InFloat64Out(in *float64) *float64 { + return in +} + +func (GreetService) BoolInBoolOut(in bool) bool { + return in +} + +func (GreetService) PointerBoolInBoolOut(in *bool) *bool { + return in +} + +func (GreetService) PointerStringInStringOut(in *string) *string { + return in +} + +func (GreetService) StructPointerInputErrorOutput(in *Person) error { + return nil +} + +func (GreetService) StructInputStructOutput(in Person) Person { + return in +} + +func (GreetService) StructPointerInputStructPointerOutput(in *Person) *Person { + return in +} + +func (GreetService) MapIntInt(in map[int]int) { +} + +func (GreetService) PointerMapIntInt(in *map[int]int) { +} + +func (GreetService) MapIntIntPointer(in map[int]*int) { +} + +func (GreetService) MapIntSliceInt(in map[int][]int) { +} + +func (GreetService) MapIntSliceIntInMapIntSliceIntOut(in map[int][]int) (out map[int][]int) { + return nil +} + +func (GreetService) ArrayInt(in [4]int) { +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/struct_literal_single/bound_types.json b/v3/internal/generator/testcases/struct_literal_single/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_single/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/struct_literal_single/main.go b/v3/internal/generator/testcases/struct_literal_single/main.go new file mode 100644 index 000000000..945874a0b --- /dev/null +++ b/v3/internal/generator/testcases/struct_literal_single/main.go @@ -0,0 +1,205 @@ +package main + +import ( + _ "embed" + "log" + "strings" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Person struct { + Name string + Parent *Person + Details struct { + Age int + Address struct { + Street string + } + } +} + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +func (*GreetService) NoInputsStringOut() string { + return "Hello" +} + +func (*GreetService) StringArrayInputStringOut(in []string) string { + return strings.Join(in, ",") +} + +func (*GreetService) StringArrayInputStringArrayOut(in []string) []string { + return in +} + +func (*GreetService) StringArrayInputNamedOutput(in []string) (output []string) { + return in +} + +func (*GreetService) StringArrayInputNamedOutputs(in []string) (output []string, err error) { + return in, nil +} + +func (*GreetService) IntPointerInputNamedOutputs(in *int) (output *int, err error) { + return in, nil +} + +func (*GreetService) UIntPointerInAndOutput(in *uint) *uint { + return in +} + +func (*GreetService) UInt8PointerInAndOutput(in *uint8) *uint8 { + return in +} + +func (*GreetService) UInt16PointerInAndOutput(in *uint16) *uint16 { + return in +} + +func (*GreetService) UInt32PointerInAndOutput(in *uint32) *uint32 { + return in +} + +func (*GreetService) UInt64PointerInAndOutput(in *uint64) *uint64 { + return in +} + +func (*GreetService) IntPointerInAndOutput(in *int) *int { + return in +} + +func (*GreetService) Int8PointerInAndOutput(in *int8) *int8 { + return in +} + +func (*GreetService) Int16PointerInAndOutput(in *int16) *int16 { + return in +} + +func (*GreetService) Int32PointerInAndOutput(in *int32) *int32 { + return in +} + +func (*GreetService) Int64PointerInAndOutput(in *int64) *int64 { + return in +} + +func (*GreetService) IntInIntOut(in int) int { + return in +} + +func (*GreetService) Int8InIntOut(in int8) int8 { + return in +} +func (*GreetService) Int16InIntOut(in int16) int16 { + return in +} +func (*GreetService) Int32InIntOut(in int32) int32 { + return in +} +func (*GreetService) Int64InIntOut(in int64) int64 { + return in +} + +func (*GreetService) UIntInUIntOut(in uint) uint { + return in +} + +func (*GreetService) UInt8InUIntOut(in uint8) uint8 { + return in +} +func (*GreetService) UInt16InUIntOut(in uint16) uint16 { + return in +} +func (*GreetService) UInt32InUIntOut(in uint32) uint32 { + return in +} +func (*GreetService) UInt64InUIntOut(in uint64) uint64 { + return in +} + +func (*GreetService) Float32InFloat32Out(in float32) float32 { + return in +} + +func (*GreetService) Float64InFloat64Out(in float64) float64 { + return in +} + +func (*GreetService) PointerFloat32InFloat32Out(in *float32) *float32 { + return in +} + +func (*GreetService) PointerFloat64InFloat64Out(in *float64) *float64 { + return in +} + +func (*GreetService) BoolInBoolOut(in bool) bool { + return in +} + +func (*GreetService) PointerBoolInBoolOut(in *bool) *bool { + return in +} + +func (*GreetService) PointerStringInStringOut(in *string) *string { + return in +} + +func (*GreetService) StructPointerInputErrorOutput(in *Person) error { + return nil +} + +func (*GreetService) StructInputStructOutput(in Person) Person { + return in +} + +func (*GreetService) StructPointerInputStructPointerOutput(in *Person) *Person { + return in +} + +func (*GreetService) MapIntInt(in map[int]int) { +} + +func (*GreetService) PointerMapIntInt(in *map[int]int) { +} + +func (*GreetService) MapIntIntPointer(in map[int]*int) { +} + +func (*GreetService) MapIntSliceInt(in map[int][]int) { +} + +func (*GreetService) MapIntSliceIntInMapIntSliceIntOut(in map[int][]int) (out map[int][]int) { + return nil +} + +func (*GreetService) ArrayInt(in [4]int) { +} + +func main() { + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/variable_single/bound_types.json b/v3/internal/generator/testcases/variable_single/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/variable_single/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/variable_single/main.go b/v3/internal/generator/testcases/variable_single/main.go new file mode 100644 index 000000000..5baf1a04f --- /dev/null +++ b/v3/internal/generator/testcases/variable_single/main.go @@ -0,0 +1,37 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +func main() { + greetService := application.NewService(&GreetService{}) + app := application.New(application.Options{ + Services: []application.Service{ + greetService, + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/variable_single_from_function/bound_types.json b/v3/internal/generator/testcases/variable_single_from_function/bound_types.json new file mode 100644 index 000000000..a8b95e321 --- /dev/null +++ b/v3/internal/generator/testcases/variable_single_from_function/bound_types.json @@ -0,0 +1,3 @@ +[ + ".GreetService" +] diff --git a/v3/internal/generator/testcases/variable_single_from_function/main.go b/v3/internal/generator/testcases/variable_single_from_function/main.go new file mode 100644 index 000000000..247702051 --- /dev/null +++ b/v3/internal/generator/testcases/variable_single_from_function/main.go @@ -0,0 +1,42 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string +} + +// Greet someone +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +func NewGreetService() application.Service { + return application.NewService(&GreetService{}) +} + +func main() { + greetService := NewGreetService() + app := application.New(application.Options{ + Services: []application.Service{ + greetService, + }, + }) + + _ = app.Window.New() // discard + // or: win := app.Window.New() // keep for later + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/variable_single_from_other_function/bound_types.json b/v3/internal/generator/testcases/variable_single_from_other_function/bound_types.json new file mode 100644 index 000000000..86a0a8812 --- /dev/null +++ b/v3/internal/generator/testcases/variable_single_from_other_function/bound_types.json @@ -0,0 +1,4 @@ +[ + ".GreetService", + "/services.OtherService" +] diff --git a/v3/internal/generator/testcases/variable_single_from_other_function/main.go b/v3/internal/generator/testcases/variable_single_from_other_function/main.go new file mode 100644 index 000000000..6bbb73f46 --- /dev/null +++ b/v3/internal/generator/testcases/variable_single_from_other_function/main.go @@ -0,0 +1,53 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// GreetService is great +type GreetService struct { + SomeVariable int + lowerCase string + target *Person +} + +// Person is a person! +// They have a name and an address +type Person struct { + Name string + Address *services.Address +} + +// Greet does XYZ +func (*GreetService) Greet(name string) string { + return "Hello " + name +} + +// NewPerson creates a new person +func (*GreetService) NewPerson(name string) *Person { + return &Person{Name: name} +} + +func main() { + otherService := services.NewOtherService() + app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(&GreetService{}), + otherService, + }, + }) + + app.Window.New() + + err := app.Run() + + if err != nil { + log.Fatal(err) + } + +} diff --git a/v3/internal/generator/testcases/variable_single_from_other_function/services/other.go b/v3/internal/generator/testcases/variable_single_from_other_function/services/other.go new file mode 100644 index 000000000..4ac6e6efd --- /dev/null +++ b/v3/internal/generator/testcases/variable_single_from_other_function/services/other.go @@ -0,0 +1,28 @@ +package services + +import "github.com/wailsapp/wails/v3/pkg/application" + +// OtherService is a struct +// that does things +type OtherService struct { + t int +} + +type Address struct { + Street string + State string + Country string +} + +// Yay does this and that +func (o *OtherService) Yay() *Address { + return &Address{ + Street: "123 Pitt Street", + State: "New South Wales", + Country: "Australia", + } +} + +func NewOtherService() application.Service { + return application.NewService(&OtherService{}) +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/index.js new file mode 100644 index 000000000..cf48d86db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/index.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {$models.TextMarshaler} TextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/json/index.js new file mode 100644 index 000000000..22f1fd904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/json/index.js @@ -0,0 +1,11 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {$models.Marshaler} Marshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/json/models.js new file mode 100644 index 000000000..96abf0ef1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/json/models.js @@ -0,0 +1,13 @@ +// @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 "/wails/runtime.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {any} Marshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/models.js new file mode 100644 index 000000000..59cfef5bd --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/encoding/models.js @@ -0,0 +1,15 @@ +// @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 "/wails/runtime.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {any} TextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js new file mode 100644 index 000000000..fcbd41a3a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js @@ -0,0 +1,97 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + * @param {$models.Alias} aliasValue + * @returns {$CancellablePromise<$models.Person>} + */ +export function Get(aliasValue) { + return $Call.ByID(1928502664, aliasValue).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +/** + * Apparently, aliases are all the rage right now. + * @param {$models.AliasedPerson} p + * @returns {$CancellablePromise<$models.StrangelyAliasedPerson>} + */ +export function GetButAliased(p) { + return $Call.ByID(1896499664, p).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +/** + * Get someone quite different. + * @returns {$CancellablePromise<$models.GenericPerson>} + */ +export function GetButDifferent() { + return $Call.ByID(2240931744).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +/** + * @returns {$CancellablePromise} + */ +export function GetButForeignPrivateAlias() { + return $Call.ByID(643456960).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @returns {$CancellablePromise<$models.AliasGroup>} + */ +export function GetButGenericAliases() { + return $Call.ByID(914093800).then(/** @type {($result: any) => any} */(($result) => { + return $$createType3($result); + })); +} + +/** + * Greet a lot of unusual things. + * @param {$models.EmptyAliasStruct} $0 + * @param {$models.EmptyStruct} $1 + * @returns {$CancellablePromise<$models.AliasStruct>} + */ +export function Greet($0, $1) { + return $Call.ByID(1411160069, $0, $1).then(/** @type {($result: any) => any} */(($result) => { + return $$createType7($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.GenericPerson.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; +const $$createType3 = $models.AliasGroup.createFrom; +const $$createType4 = $Create.Array($Create.Any); +const $$createType5 = $Create.Array($Create.Any); +const $$createType6 = $Create.Struct({ + "NoMoreIdeas": $$createType5, +}); +const $$createType7 = $Create.Struct({ + "Foo": $$createType4, + "Other": $$createType6, +}); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js new file mode 100644 index 000000000..4278c7958 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js @@ -0,0 +1,61 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + AliasGroup, + AliasedPerson, + EmptyStruct, + GenericPerson, + GenericPersonAlias, + IndirectPersonAlias, + Person, + StrangelyAliasedPerson, + TPIndirectPersonAlias +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * A nice type Alias. + * @typedef {$models.Alias} Alias + */ + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {$models.AliasStruct} AliasStruct + */ + +/** + * An empty struct alias. + * @typedef {$models.EmptyAliasStruct} EmptyAliasStruct + */ + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {$models.GenericAlias} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {$models.GenericMapAlias} GenericMapAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {$models.GenericPtrAlias} GenericPtrAlias + */ + +/** + * Another struct alias. + * @typedef {$models.OtherAliasStruct} OtherAliasStruct + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js new file mode 100644 index 000000000..3de57786d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js @@ -0,0 +1,334 @@ +// @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 "/wails/runtime.js"; + +/** + * A nice type Alias. + * @typedef {number} Alias + */ + +/** + * A class whose fields have various aliased types. + */ +export class AliasGroup { + /** + * Creates a new AliasGroup instance. + * @param {Partial} [$$source = {}] - The source object to create the AliasGroup. + */ + constructor($$source = {}) { + if (!("GAi" in $$source)) { + /** + * @member + * @type {GenericAlias} + */ + this["GAi"] = 0; + } + if (!("GAP" in $$source)) { + /** + * @member + * @type {GenericAlias>} + */ + this["GAP"] = (new GenericPerson()); + } + if (!("GPAs" in $$source)) { + /** + * @member + * @type {GenericPtrAlias} + */ + this["GPAs"] = null; + } + if (!("GPAP" in $$source)) { + /** + * @member + * @type {GenericPtrAlias>} + */ + this["GPAP"] = null; + } + if (!("GMA" in $$source)) { + /** + * @member + * @type {GenericMapAlias} + */ + this["GMA"] = {}; + } + if (!("GPA" in $$source)) { + /** + * @member + * @type {GenericPersonAlias} + */ + this["GPA"] = (new GenericPersonAlias()); + } + if (!("IPA" in $$source)) { + /** + * @member + * @type {IndirectPersonAlias} + */ + this["IPA"] = (new IndirectPersonAlias()); + } + if (!("TPIPA" in $$source)) { + /** + * @member + * @type {TPIndirectPersonAlias} + */ + this["TPIPA"] = (new TPIndirectPersonAlias()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new AliasGroup instance from a string or object. + * @param {any} [$$source = {}] + * @returns {AliasGroup} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType0; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType5; + const $$createField4_0 = $$createType6; + const $$createField5_0 = $$createType8; + const $$createField6_0 = $$createType8; + const $$createField7_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("GAP" in $$parsedSource) { + $$parsedSource["GAP"] = $$createField1_0($$parsedSource["GAP"]); + } + if ("GPAs" in $$parsedSource) { + $$parsedSource["GPAs"] = $$createField2_0($$parsedSource["GPAs"]); + } + if ("GPAP" in $$parsedSource) { + $$parsedSource["GPAP"] = $$createField3_0($$parsedSource["GPAP"]); + } + if ("GMA" in $$parsedSource) { + $$parsedSource["GMA"] = $$createField4_0($$parsedSource["GMA"]); + } + if ("GPA" in $$parsedSource) { + $$parsedSource["GPA"] = $$createField5_0($$parsedSource["GPA"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField6_0($$parsedSource["IPA"]); + } + if ("TPIPA" in $$parsedSource) { + $$parsedSource["TPIPA"] = $$createField7_0($$parsedSource["TPIPA"]); + } + return new AliasGroup(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {Object} AliasStruct + * @property {number[]} Foo - A field with a comment. + * @property {string} [Bar] - Definitely not Foo. + * @property {string} [Baz] - Definitely not Foo. + * @property {OtherAliasStruct} Other - A nested alias struct. + */ + +/** + * An empty struct alias. + * @typedef { { + * } } EmptyAliasStruct + */ + +/** + * An empty struct. + */ +export class EmptyStruct { + /** + * Creates a new EmptyStruct instance. + * @param {Partial} [$$source = {}] - The source object to create the EmptyStruct. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new EmptyStruct instance from a string or object. + * @param {any} [$$source = {}] + * @returns {EmptyStruct} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new EmptyStruct(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {T} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {{ [_: string]: U }} GenericMapAlias + */ + +/** + * A generic struct containing an alias. + * @template T + */ +export class GenericPerson { + /** + * Creates a new GenericPerson instance. + * @param {Partial>} [$$source = {}] - The source object to create the GenericPerson. + */ + constructor($$source = {}) { + if (/** @type {any} */(false)) { + /** + * @member + * @type {T | undefined} + */ + this["Name"] = undefined; + } + if (!("AliasedField" in $$source)) { + /** + * @member + * @type {Alias} + */ + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class GenericPerson. + * @template [T=any] + * @param {(source: any) => T} $$createParamT + * @returns {($$source?: any) => GenericPerson} + */ + static createFrom($$createParamT) { + const $$createField0_0 = $$createParamT; + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Name" in $$parsedSource) { + $$parsedSource["Name"] = $$createField0_0($$parsedSource["Name"]); + } + return new GenericPerson(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +/** + * A generic alias that wraps a generic struct. + */ +export const GenericPersonAlias = GenericPerson; + +/** + * A generic alias that wraps a generic struct. + * @template T + * @typedef {GenericPerson[]>} GenericPersonAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {GenericAlias | null} GenericPtrAlias + */ + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export const IndirectPersonAlias = GenericPersonAlias; + +/** + * An alias that wraps a class through a non-typeparam alias. + * @typedef {GenericPersonAlias} IndirectPersonAlias + */ + +/** + * Another struct alias. + * @typedef {Object} OtherAliasStruct + * @property {number[]} NoMoreIdeas + */ + +/** + * A non-generic struct containing an alias. + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * The Person's name. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("AliasedField" in $$source)) { + /** + * A random alias field. + * @member + * @type {Alias} + */ + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * A class alias. + */ +export const AliasedPerson = Person; + +/** + * A class alias. + * @typedef {Person} AliasedPerson + */ + +/** + * Another class alias, but ordered after its aliased class. + */ +export const StrangelyAliasedPerson = Person; + +/** + * Another class alias, but ordered after its aliased class. + * @typedef {Person} StrangelyAliasedPerson + */ + +/** + * An alias that wraps a class through a typeparam alias. + */ +export const TPIndirectPersonAlias = GenericPerson; + +/** + * An alias that wraps a class through a typeparam alias. + * @typedef {GenericAlias>} TPIndirectPersonAlias + */ + +// Private type creation functions +const $$createType0 = GenericPerson.createFrom($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = $Create.Nullable($$createType1); +const $$createType3 = $Create.Array($Create.Any); +const $$createType4 = GenericPerson.createFrom($$createType3); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Array($Create.Any); +const $$createType8 = GenericPerson.createFrom($$createType7); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js new file mode 100644 index 000000000..f6c839720 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js new file mode 100644 index 000000000..42219054f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod() { + return $Call.ByID(2241101727); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js new file mode 100644 index 000000000..5e27fbc9e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod2() { + return $Call.ByID(1556848345); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js new file mode 100644 index 000000000..fa634943d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {$models.Person} person + * @param {$models.Embedded1} emb + * @returns {$CancellablePromise} + */ +export function Greet(person, emb) { + return $Call.ByID(1411160069, person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js new file mode 100644 index 000000000..ab78e5ea3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Embedded1, + Person, + Title +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Embedded3} Embedded3 + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js new file mode 100644 index 000000000..7a0edf1c7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js @@ -0,0 +1,272 @@ +// @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 "/wails/runtime.js"; + +export class Embedded1 { + /** + * Creates a new Embedded1 instance. + * @param {Partial} [$$source = {}] - The source object to create the Embedded1. + */ + constructor($$source = {}) { + if (!("Friends" in $$source)) { + /** + * Friends should be shadowed in Person by a field of lesser depth + * @member + * @type {number} + */ + this["Friends"] = 0; + } + if (!("Vanish" in $$source)) { + /** + * Vanish should be omitted from Person because there is another field with same depth and no tag + * @member + * @type {number} + */ + this["Vanish"] = 0; + } + if (!("StillThere" in $$source)) { + /** + * StillThere should be shadowed in Person by other field with same depth and a json tag + * @member + * @type {string} + */ + this["StillThere"] = ""; + } + if (!("NamingThingsIsHard" in $$source)) { + /** + * NamingThingsIsHard is a law of programming + * @member + * @type {`${boolean}`} + */ + this["NamingThingsIsHard"] = "false"; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Embedded1 instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Embedded1} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Embedded1(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * @typedef {string} Embedded3 + */ + +/** + * Person represents a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (/** @type {any} */(false)) { + /** + * Titles is optional in JSON + * @member + * @type {Title[] | undefined} + */ + this["Titles"] = undefined; + } + if (!("Names" in $$source)) { + /** + * Names has a + * multiline comment + * @member + * @type {string[]} + */ + this["Names"] = []; + } + if (!("Partner" in $$source)) { + /** + * Partner has a custom and complex JSON key + * @member + * @type {Person | null} + */ + this["Partner"] = null; + } + if (!("Friends" in $$source)) { + /** + * @member + * @type {(Person | null)[]} + */ + this["Friends"] = []; + } + if (!("NamingThingsIsHard" in $$source)) { + /** + * NamingThingsIsHard is a law of programming + * @member + * @type {`${boolean}`} + */ + this["NamingThingsIsHard"] = "false"; + } + if (!("StillThere" in $$source)) { + /** + * StillThereButRenamed should shadow in Person the other field with same depth and no json tag + * @member + * @type {Embedded3 | null} + */ + this["StillThere"] = null; + } + if (!("-" in $$source)) { + /** + * StrangeNumber maps to "-" + * @member + * @type {number} + */ + this["-"] = 0; + } + if (!("Embedded3" in $$source)) { + /** + * Embedded3 should appear with key "Embedded3" + * @member + * @type {Embedded3} + */ + this["Embedded3"] = ""; + } + if (!("StrangerNumber" in $$source)) { + /** + * StrangerNumber is serialized as a string + * @member + * @type {`${number}`} + */ + this["StrangerNumber"] = "0"; + } + if (/** @type {any} */(false)) { + /** + * StrangestString is optional and serialized as a JSON string + * @member + * @type {`"${string}"` | undefined} + */ + this["StrangestString"] = undefined; + } + if (/** @type {any} */(false)) { + /** + * StringStrangest is serialized as a JSON string and optional + * @member + * @type {`"${string}"` | undefined} + */ + this["StringStrangest"] = undefined; + } + if (/** @type {any} */(false)) { + /** + * embedded4 should be optional and appear with key "emb4" + * @member + * @type {embedded4 | undefined} + */ + this["emb4"] = undefined; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType3; + const $$createField3_0 = $$createType4; + const $$createField11_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Titles" in $$parsedSource) { + $$parsedSource["Titles"] = $$createField0_0($$parsedSource["Titles"]); + } + if ("Names" in $$parsedSource) { + $$parsedSource["Names"] = $$createField1_0($$parsedSource["Names"]); + } + if ("Partner" in $$parsedSource) { + $$parsedSource["Partner"] = $$createField2_0($$parsedSource["Partner"]); + } + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField3_0($$parsedSource["Friends"]); + } + if ("emb4" in $$parsedSource) { + $$parsedSource["emb4"] = $$createField11_0($$parsedSource["emb4"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +export class embedded4 { + /** + * Creates a new embedded4 instance. + * @param {Partial} [$$source = {}] - The source object to create the embedded4. + */ + constructor($$source = {}) { + if (!("NamingThingsIsHard" in $$source)) { + /** + * NamingThingsIsHard is a law of programming + * @member + * @type {`${boolean}`} + */ + this["NamingThingsIsHard"] = "false"; + } + if (!("Friends" in $$source)) { + /** + * Friends should not be shadowed in Person as embedded4 is not embedded + * from encoding/json's point of view; + * however, it should be shadowed in Embedded1 + * @member + * @type {boolean} + */ + this["Friends"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new embedded4 instance from a string or object. + * @param {any} [$$source = {}] + * @returns {embedded4} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new embedded4(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = Person.createFrom; +const $$createType3 = $Create.Nullable($$createType2); +const $$createType4 = $Create.Array($$createType3); +const $$createType5 = embedded4.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js new file mode 100644 index 000000000..35fac10f2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + * @param {string} str + * @param {$models.Person[]} people + * @param {{"AnotherCount": number, "AnotherOne": $models.Person | null}} $2 + * @param {{ [_: `${number}`]: boolean | null }} assoc + * @param {(number | null)[]} $4 + * @param {string[]} other + * @returns {$CancellablePromise<[$models.Person, any, number[]]>} + */ +export function Greet(str, people, $2, assoc, $4, ...other) { + return $Call.ByID(1411160069, str, people, $2, assoc, $4, other).then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[2] = $$createType1($result[2]); + return $result; + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Array($Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js new file mode 100644 index 000000000..82af81baf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js @@ -0,0 +1,38 @@ +// @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 "/wails/runtime.js"; + +/** + * Person represents a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js new file mode 100644 index 000000000..29255dd9c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.StructA, $models.StructC]>} + */ +export function MakeCycles() { + return $Call.ByID(440020721).then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + return $result; + })); +} + +// Private type creation functions +const $$createType0 = $models.StructA.createFrom; +const $$createType1 = $models.StructC.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js new file mode 100644 index 000000000..0ad0efb4e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + StructA, + StructC, + StructE +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js new file mode 100644 index 000000000..f24f5a2c9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js @@ -0,0 +1,164 @@ +// @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 "/wails/runtime.js"; + +export class StructA { + /** + * Creates a new StructA instance. + * @param {Partial} [$$source = {}] - The source object to create the StructA. + */ + constructor($$source = {}) { + if (!("B" in $$source)) { + /** + * @member + * @type {structB | null} + */ + this["B"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructA instance from a string or object. + * @param {any} [$$source = {}] + * @returns {StructA} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("B" in $$parsedSource) { + $$parsedSource["B"] = $$createField0_0($$parsedSource["B"]); + } + return new StructA(/** @type {Partial} */($$parsedSource)); + } +} + +export class StructC { + /** + * Creates a new StructC instance. + * @param {Partial} [$$source = {}] - The source object to create the StructC. + */ + constructor($$source = {}) { + if (!("D" in $$source)) { + /** + * @member + * @type {structD} + */ + this["D"] = (new structD()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructC instance from a string or object. + * @param {any} [$$source = {}] + * @returns {StructC} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType2; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("D" in $$parsedSource) { + $$parsedSource["D"] = $$createField0_0($$parsedSource["D"]); + } + return new StructC(/** @type {Partial} */($$parsedSource)); + } +} + +export class StructE { + /** + * Creates a new StructE instance. + * @param {Partial} [$$source = {}] - The source object to create the StructE. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new StructE instance from a string or object. + * @param {any} [$$source = {}] + * @returns {StructE} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new StructE(/** @type {Partial} */($$parsedSource)); + } +} + +export class structB { + /** + * Creates a new structB instance. + * @param {Partial} [$$source = {}] - The source object to create the structB. + */ + constructor($$source = {}) { + if (!("A" in $$source)) { + /** + * @member + * @type {StructA | null} + */ + this["A"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structB instance from a string or object. + * @param {any} [$$source = {}] + * @returns {structB} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType4; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField0_0($$parsedSource["A"]); + } + return new structB(/** @type {Partial} */($$parsedSource)); + } +} + +export class structD { + /** + * Creates a new structD instance. + * @param {Partial} [$$source = {}] - The source object to create the structD. + */ + constructor($$source = {}) { + if (!("E" in $$source)) { + /** + * @member + * @type {StructE} + */ + this["E"] = (new StructE()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structD instance from a string or object. + * @param {any} [$$source = {}] + * @returns {structD} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("E" in $$parsedSource) { + $$parsedSource["E"] = $$createField0_0($$parsedSource["E"]); + } + return new structD(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = structB.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = structD.createFrom; +const $$createType3 = StructA.createFrom; +const $$createType4 = $Create.Nullable($$createType3); +const $$createType5 = StructE.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js new file mode 100644 index 000000000..faf090884 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js @@ -0,0 +1,65 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]>} + */ +export function MakeCycles() { + return $Call.ByID(440020721).then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType9($result[1]); + return $result; + })); +} + +// Private type creation functions +var $$createType0 = /** @type {(...args: any[]) => any} */(function $$initCreateType0(...args) { + if ($$createType0 === $$initCreateType0) { + $$createType0 = $$createType3; + } + return $$createType0(...args); +}); +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = $Create.Map($Create.Any, $$createType1); +const $$createType3 = $Create.Array($$createType2); +var $$createType4 = /** @type {(...args: any[]) => any} */(function $$initCreateType4(...args) { + if ($$createType4 === $$initCreateType4) { + $$createType4 = $$createType8; + } + return $$createType4(...args); +}); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Array($Create.Any); +const $$createType7 = $Create.Struct({ + "X": $$createType5, + "Y": $$createType6, +}); +const $$createType8 = $Create.Array($$createType7); +var $$createType9 = /** @type {(...args: any[]) => any} */(function $$initCreateType9(...args) { + if ($$createType9 === $$initCreateType9) { + $$createType9 = $$createType13; + } + return $$createType9(...args); +}); +const $$createType10 = $Create.Nullable($$createType9); +const $$createType11 = $Create.Array($$createType4); +const $$createType12 = $Create.Struct({ + "X": $$createType10, + "Y": $$createType11, +}); +const $$createType13 = $Create.Array($$createType12); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js new file mode 100644 index 000000000..9fc31bf7c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js @@ -0,0 +1,23 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Alias} Alias + */ + +/** + * @typedef {$models.Cyclic} Cyclic + */ + +/** + * @template T + * @typedef {$models.GenericCyclic} GenericCyclic + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js new file mode 100644 index 000000000..47d41f572 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js @@ -0,0 +1,20 @@ +// @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 "/wails/runtime.js"; + +/** + * @typedef {Cyclic | null} Alias + */ + +/** + * @typedef {{ [_: string]: Alias }[]} Cyclic + */ + +/** + * @template T + * @typedef {{"X": GenericCyclic | null, "Y": T[]}[]} GenericCyclic + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js new file mode 100644 index 000000000..972196ce3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Classes!"); +console.log("Hello JS!"); +console.log("Hello JS Classes!"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js new file mode 100644 index 000000000..fce17fb1d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.InternalModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByID(538079117, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js new file mode 100644 index 000000000..291a3cecf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js @@ -0,0 +1,69 @@ +// @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 "/wails/runtime.js"; + +/** + * An exported but internal model. + */ +export class InternalModel { + /** + * Creates a new InternalModel instance. + * @param {Partial} [$$source = {}] - The source object to create the InternalModel. + */ + constructor($$source = {}) { + if (!("Field" in $$source)) { + /** + * @member + * @type {string} + */ + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new InternalModel instance from a string or object. + * @param {any} [$$source = {}] + * @returns {InternalModel} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new InternalModel(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * An unexported model. + */ +export class unexportedModel { + /** + * Creates a new unexportedModel instance. + * @param {Partial} [$$source = {}] - The source object to create the unexportedModel. + */ + constructor($$source = {}) { + if (!("Field" in $$source)) { + /** + * @member + * @type {string} + */ + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new unexportedModel instance from a string or object. + * @param {any} [$$source = {}] + * @returns {unexportedModel} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new unexportedModel(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js new file mode 100644 index 000000000..b7e83cfd4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Dummy +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js new file mode 100644 index 000000000..274f4eed4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js @@ -0,0 +1,28 @@ +// @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 "/wails/runtime.js"; + +export class Dummy { + /** + * Creates a new Dummy instance. + * @param {Partial} [$$source = {}] - The source object to create the Dummy. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new Dummy instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Dummy} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Dummy(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_j.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_j.js new file mode 100644 index 000000000..2166d33b6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_jc.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_jc.js new file mode 100644 index 000000000..338898726 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_jc.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("JS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js new file mode 100644 index 000000000..cc7ed89d3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js @@ -0,0 +1,35 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +/** + * @param {string} $0 + * @returns {$CancellablePromise} + */ +function InternalMethod($0) { + return $Call.ByID(3518775569, $0); +} + +/** + * @param {otherpackage$0.Dummy} $0 + * @returns {$CancellablePromise} + */ +export function VisibleMethod($0) { + return $Call.ByID(474018228, $0); +} + +/** + * @param {string} arg + * @returns {Promise} + */ +export async function CustomMethod(arg) { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js new file mode 100644 index 000000000..724e79e12 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Classes"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js new file mode 100644 index 000000000..b2f9c5edb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_jc.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_jc.js new file mode 100644 index 000000000..ddf4920e5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_jc.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js new file mode 100644 index 000000000..d3f53be87 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.unexportedModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByID(37626172, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js new file mode 100644 index 000000000..a178744b9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js @@ -0,0 +1,53 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Comment 1. + * @returns {$CancellablePromise} + */ +export function Method1() { + return $Call.ByID(841558284); +} + +/** + * Comment 2. + * @returns {$CancellablePromise} + */ +export function Method2() { + return $Call.ByID(891891141); +} + +/** + * Comment 3a. + * Comment 3b. + * @returns {$CancellablePromise} + */ +export function Method3() { + return $Call.ByID(875113522); +} + +/** + * Comment 4. + * @returns {$CancellablePromise} + */ +export function Method4() { + return $Call.ByID(791225427); +} + +/** + * Comment 5. + * @returns {$CancellablePromise} + */ +export function Method5() { + return $Call.ByID(774447808); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js new file mode 100644 index 000000000..ed402a8b2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js @@ -0,0 +1,41 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {$models.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByID(1411160069, name, title); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js new file mode 100644 index 000000000..d5d66d4cb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Person, + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js new file mode 100644 index 000000000..2c5df9ee7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js @@ -0,0 +1,98 @@ +// @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 "/wails/runtime.js"; + +/** + * Age is an integer with some predefined values + * @typedef {number} Age + */ + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Title" in $$source)) { + /** + * @member + * @type {Title} + */ + this["Title"] = Title.$zero; + } + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Age" in $$source)) { + /** + * @member + * @type {Age} + */ + this["Age"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js new file mode 100644 index 000000000..fbc2294e9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {services$0.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByID(1411160069, name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js new file mode 100644 index 000000000..089a8b685 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js new file mode 100644 index 000000000..65ebfa2f7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js @@ -0,0 +1,27 @@ +// @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 "/wails/runtime.js"; + +/** + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js new file mode 100644 index 000000000..50737a34b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js new file mode 100644 index 000000000..0ab295133 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js @@ -0,0 +1,57 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {services$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js new file mode 100644 index 000000000..1866aca09 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(2007737399).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js new file mode 100644 index 000000000..50737a34b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js new file mode 100644 index 000000000..29a95e11e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js @@ -0,0 +1,54 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./services/other/models.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {other$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = other$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js new file mode 100644 index 000000000..293a2f0bb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(2447353446).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js new file mode 100644 index 000000000..e50a4a6ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js new file mode 100644 index 000000000..9dfe48511 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js new file mode 100644 index 000000000..1bc7cb45b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js @@ -0,0 +1,30 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function GreetWithContext(name) { + return $Call.ByID(1310150960, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js new file mode 100644 index 000000000..9dfe48511 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js new file mode 100644 index 000000000..9fd9745c1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js @@ -0,0 +1,97 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Maps +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * @template S + * @typedef {$models.BasicCstrAlias} BasicCstrAlias + */ + +/** + * @template R + * @typedef {$models.ComparableCstrAlias} ComparableCstrAlias + */ + +/** + * @typedef {$models.EmbeddedCustomInterface} EmbeddedCustomInterface + */ + +/** + * @typedef {$models.EmbeddedOriginalInterface} EmbeddedOriginalInterface + */ + +/** + * @typedef {$models.EmbeddedPointer} EmbeddedPointer + */ + +/** + * @typedef {$models.EmbeddedPointerPtr} EmbeddedPointerPtr + */ + +/** + * @typedef {$models.EmbeddedValue} EmbeddedValue + */ + +/** + * @typedef {$models.EmbeddedValuePtr} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {$models.GoodTildeCstrAlias} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {$models.InterfaceCstrAlias} InterfaceCstrAlias + */ + +/** + * @template X + * @typedef {$models.MixedCstrAlias} MixedCstrAlias + */ + +/** + * @template V + * @typedef {$models.NonBasicCstrAlias} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {$models.PointableCstrAlias} PointableCstrAlias + */ + +/** + * @typedef {$models.PointerAlias} PointerAlias + */ + +/** + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * @typedef {$models.StringAlias} StringAlias + */ + +/** + * @typedef {$models.StringType} StringType + */ + +/** + * @typedef {$models.ValueAlias} ValueAlias + */ + +/** + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js new file mode 100644 index 000000000..1dc5bfc38 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js @@ -0,0 +1,1957 @@ +// @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 "/wails/runtime.js"; + +/** + * @template S + * @typedef {S} BasicCstrAlias + */ + +/** + * @template R + * @typedef {R} ComparableCstrAlias + */ + +/** + * @typedef {string} EmbeddedCustomInterface + */ + +/** + * @typedef {string} EmbeddedOriginalInterface + */ + +/** + * @typedef {string} EmbeddedPointer + */ + +/** + * @typedef {string} EmbeddedPointerPtr + */ + +/** + * @typedef {string} EmbeddedValue + */ + +/** + * @typedef {string} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {U} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {Y} InterfaceCstrAlias + */ + +/** + * @template R,S,T,U,V,W,X,Y,Z + */ +export class Maps { + /** + * Creates a new Maps instance. + * @param {Partial>} [$$source = {}] - The source object to create the Maps. + */ + constructor($$source = {}) { + if (!("Bool" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["Bool"] = {}; + } + if (!("Int" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Int"] = {}; + } + if (!("Uint" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Uint"] = {}; + } + if (!("Float" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["Float"] = {}; + } + if (!("Complex" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["Complex"] = {}; + } + if (!("Byte" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Byte"] = {}; + } + if (!("Rune" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Rune"] = {}; + } + if (!("String" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: string]: number }} + */ + this["String"] = {}; + } + if (!("IntPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IntPtr"] = {}; + } + if (!("UintPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["UintPtr"] = {}; + } + if (!("FloatPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["FloatPtr"] = {}; + } + if (!("ComplexPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["ComplexPtr"] = {}; + } + if (!("StringPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["StringPtr"] = {}; + } + if (!("NTM" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["NTM"] = {}; + } + if (!("NTMPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["NTMPtr"] = {}; + } + if (!("VTM" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueTextMarshaler]: number }} + */ + this["VTM"] = {}; + } + if (!("VTMPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueTextMarshaler]: number }} + */ + this["VTMPtr"] = {}; + } + if (!("PTM" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PTM"] = {}; + } + if (!("PTMPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointerTextMarshaler]: number }} + */ + this["PTMPtr"] = {}; + } + if (!("JTM" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["JTM"] = {}; + } + if (!("JTMPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["JTMPtr"] = {}; + } + if (!("A" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["A"] = {}; + } + if (!("APtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["APtr"] = {}; + } + if (!("TM" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TM"] = {}; + } + if (!("TMPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["TMPtr"] = {}; + } + if (!("CI" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["CI"] = {}; + } + if (!("CIPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["CIPtr"] = {}; + } + if (!("EI" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["EI"] = {}; + } + if (!("EIPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["EIPtr"] = {}; + } + if (!("EV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValue]: number }} + */ + this["EV"] = {}; + } + if (!("EVPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValue]: number }} + */ + this["EVPtr"] = {}; + } + if (!("EVP" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValuePtr]: number }} + */ + this["EVP"] = {}; + } + if (!("EVPPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValuePtr]: number }} + */ + this["EVPPtr"] = {}; + } + if (!("EP" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["EP"] = {}; + } + if (!("EPPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedPointer]: number }} + */ + this["EPPtr"] = {}; + } + if (!("EPP" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedPointerPtr]: number }} + */ + this["EPP"] = {}; + } + if (!("EPPPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedPointerPtr]: number }} + */ + this["EPPPtr"] = {}; + } + if (!("ECI" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedCustomInterface]: number }} + */ + this["ECI"] = {}; + } + if (!("ECIPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedCustomInterface]: number }} + */ + this["ECIPtr"] = {}; + } + if (!("EOI" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedOriginalInterface]: number }} + */ + this["EOI"] = {}; + } + if (!("EOIPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedOriginalInterface]: number }} + */ + this["EOIPtr"] = {}; + } + if (!("WT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["WT"] = {}; + } + if (!("WA" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["WA"] = {}; + } + if (!("ST" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: StringType]: number }} + */ + this["ST"] = {}; + } + if (!("SA" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: StringAlias]: number }} + */ + this["SA"] = {}; + } + if (!("IntT" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["IntT"] = {}; + } + if (!("IntA" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["IntA"] = {}; + } + if (!("VT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VT"] = {}; + } + if (!("VTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VTPtr"] = {}; + } + if (!("VPT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VPT"] = {}; + } + if (!("VPTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VPTPtr"] = {}; + } + if (!("VA" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueAlias]: number }} + */ + this["VA"] = {}; + } + if (!("VAPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueAlias]: number }} + */ + this["VAPtr"] = {}; + } + if (!("VPA" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["VPA"] = {}; + } + if (!("VPAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VPAPtr"] = {}; + } + if (!("PT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PT"] = {}; + } + if (!("PTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PTPtr"] = {}; + } + if (!("PPT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PPT"] = {}; + } + if (!("PPTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PPTPtr"] = {}; + } + if (!("PA" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PA"] = {}; + } + if (!("PAPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointerAlias]: number }} + */ + this["PAPtr"] = {}; + } + if (!("PPA" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["PPA"] = {}; + } + if (!("PPAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PPAPtr"] = {}; + } + if (!("IT" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["IT"] = {}; + } + if (!("ITPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["ITPtr"] = {}; + } + if (!("IPT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPT"] = {}; + } + if (!("IPTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPTPtr"] = {}; + } + if (!("IA" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["IA"] = {}; + } + if (!("IAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IAPtr"] = {}; + } + if (!("IPA" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPA"] = {}; + } + if (!("IPAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPAPtr"] = {}; + } + if (!("TPR" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPR"] = {}; + } + if (!("TPRPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPRPtr"] = {}; + } + if (!("TPS" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPS"] = {}; + } + if (!("TPSPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPSPtr"] = {}; + } + if (!("TPT" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPT"] = {}; + } + if (!("TPTPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPTPtr"] = {}; + } + if (!("TPU" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPU"] = {}; + } + if (!("TPUPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPUPtr"] = {}; + } + if (!("TPV" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPV"] = {}; + } + if (!("TPVPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPVPtr"] = {}; + } + if (!("TPW" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPW"] = {}; + } + if (!("TPWPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPWPtr"] = {}; + } + if (!("TPX" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPX"] = {}; + } + if (!("TPXPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPXPtr"] = {}; + } + if (!("TPY" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPY"] = {}; + } + if (!("TPYPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPYPtr"] = {}; + } + if (!("TPZ" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPZ"] = {}; + } + if (!("TPZPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPZPtr"] = {}; + } + if (!("GAR" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAR"] = {}; + } + if (!("GARPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GARPtr"] = {}; + } + if (!("GAS" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAS"] = {}; + } + if (!("GASPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GASPtr"] = {}; + } + if (!("GAT" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAT"] = {}; + } + if (!("GATPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GATPtr"] = {}; + } + if (!("GAU" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAU"] = {}; + } + if (!("GAUPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAUPtr"] = {}; + } + if (!("GAV" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAV"] = {}; + } + if (!("GAVPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAVPtr"] = {}; + } + if (!("GAW" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAW"] = {}; + } + if (!("GAWPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAWPtr"] = {}; + } + if (!("GAX" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAX"] = {}; + } + if (!("GAXPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAXPtr"] = {}; + } + if (!("GAY" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAY"] = {}; + } + if (!("GAYPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAYPtr"] = {}; + } + if (!("GAZ" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAZ"] = {}; + } + if (!("GAZPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAZPtr"] = {}; + } + if (!("GACi" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["GACi"] = {}; + } + if (!("GACV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ComparableCstrAlias]: number }} + */ + this["GACV"] = {}; + } + if (!("GACP" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GACP"] = {}; + } + if (!("GACiPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GACiPtr"] = {}; + } + if (!("GACVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GACVPtr"] = {}; + } + if (!("GACPPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GACPPtr"] = {}; + } + if (!("GABi" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["GABi"] = {}; + } + if (!("GABs" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: BasicCstrAlias]: number }} + */ + this["GABs"] = {}; + } + if (!("GABiPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GABiPtr"] = {}; + } + if (!("GABT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GABT"] = {}; + } + if (!("GABTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GABTPtr"] = {}; + } + if (!("GAGT" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: GoodTildeCstrAlias]: number }} + */ + this["GAGT"] = {}; + } + if (!("GAGTPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAGTPtr"] = {}; + } + if (!("GANBV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: NonBasicCstrAlias]: number }} + */ + this["GANBV"] = {}; + } + if (!("GANBP" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GANBP"] = {}; + } + if (!("GANBVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GANBVPtr"] = {}; + } + if (!("GANBPPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GANBPPtr"] = {}; + } + if (!("GAPlV1" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointableCstrAlias]: number }} + */ + this["GAPlV1"] = {}; + } + if (!("GAPlV2" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointableCstrAlias]: number }} + */ + this["GAPlV2"] = {}; + } + if (!("GAPlP1" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAPlP1"] = {}; + } + if (!("GAPlP2" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointableCstrAlias]: number }} + */ + this["GAPlP2"] = {}; + } + if (!("GAPlVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPlVPtr"] = {}; + } + if (!("GAPlPPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPlPPtr"] = {}; + } + if (!("GAMi" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["GAMi"] = {}; + } + if (!("GAMS" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: MixedCstrAlias]: number }} + */ + this["GAMS"] = {}; + } + if (!("GAMV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: MixedCstrAlias]: number }} + */ + this["GAMV"] = {}; + } + if (!("GAMSPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAMSPtr"] = {}; + } + if (!("GAMVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAMVPtr"] = {}; + } + if (!("GAII" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAII"] = {}; + } + if (!("GAIV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: InterfaceCstrAlias]: number }} + */ + this["GAIV"] = {}; + } + if (!("GAIP" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAIP"] = {}; + } + if (!("GAIIPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAIIPtr"] = {}; + } + if (!("GAIVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAIVPtr"] = {}; + } + if (!("GAIPPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAIPPtr"] = {}; + } + if (!("GAPrV" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrV"] = {}; + } + if (!("GAPrP" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrP"] = {}; + } + if (!("GAPrVPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrVPtr"] = {}; + } + if (!("GAPrPPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrPPtr"] = {}; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class Maps. + * @template [R=any] + * @template [S=any] + * @template [T=any] + * @template [U=any] + * @template [V=any] + * @template [W=any] + * @template [X=any] + * @template [Y=any] + * @template [Z=any] + * @param {(source: any) => R} $$createParamR + * @param {(source: any) => S} $$createParamS + * @param {(source: any) => T} $$createParamT + * @param {(source: any) => U} $$createParamU + * @param {(source: any) => V} $$createParamV + * @param {(source: any) => W} $$createParamW + * @param {(source: any) => X} $$createParamX + * @param {(source: any) => Y} $$createParamY + * @param {(source: any) => Z} $$createParamZ + * @returns {($$source?: any) => Maps} + */ + static createFrom($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType3; + const $$createField4_0 = $$createType4; + const $$createField5_0 = $$createType5; + const $$createField6_0 = $$createType6; + const $$createField7_0 = $$createType7; + const $$createField8_0 = $$createType8; + const $$createField9_0 = $$createType9; + const $$createField10_0 = $$createType10; + const $$createField11_0 = $$createType11; + const $$createField12_0 = $$createType12; + const $$createField13_0 = $$createType13; + const $$createField14_0 = $$createType14; + const $$createField15_0 = $$createType15; + const $$createField16_0 = $$createType16; + const $$createField17_0 = $$createType17; + const $$createField18_0 = $$createType18; + const $$createField19_0 = $$createType19; + const $$createField20_0 = $$createType20; + const $$createField21_0 = $$createType21; + const $$createField22_0 = $$createType22; + const $$createField23_0 = $$createType23; + const $$createField24_0 = $$createType24; + const $$createField25_0 = $$createType25; + const $$createField26_0 = $$createType26; + const $$createField27_0 = $$createType27; + const $$createField28_0 = $$createType28; + const $$createField29_0 = $$createType29; + const $$createField30_0 = $$createType30; + const $$createField31_0 = $$createType31; + const $$createField32_0 = $$createType32; + const $$createField33_0 = $$createType33; + const $$createField34_0 = $$createType34; + const $$createField35_0 = $$createType35; + const $$createField36_0 = $$createType36; + const $$createField37_0 = $$createType37; + const $$createField38_0 = $$createType38; + const $$createField39_0 = $$createType39; + const $$createField40_0 = $$createType40; + const $$createField41_0 = $$createType41; + const $$createField42_0 = $$createType0; + const $$createField43_0 = $$createType42; + const $$createField44_0 = $$createType7; + const $$createField45_0 = $$createType43; + const $$createField46_0 = $$createType1; + const $$createField47_0 = $$createType44; + const $$createField48_0 = $$createType45; + const $$createField49_0 = $$createType46; + const $$createField50_0 = $$createType47; + const $$createField51_0 = $$createType15; + const $$createField52_0 = $$createType16; + const $$createField53_0 = $$createType16; + const $$createField54_0 = $$createType48; + const $$createField55_0 = $$createType49; + const $$createField56_0 = $$createType50; + const $$createField57_0 = $$createType51; + const $$createField58_0 = $$createType52; + const $$createField59_0 = $$createType17; + const $$createField60_0 = $$createType18; + const $$createField61_0 = $$createType18; + const $$createField62_0 = $$createType53; + const $$createField63_0 = $$createType54; + const $$createField64_0 = $$createType55; + const $$createField65_0 = $$createType56; + const $$createField66_0 = $$createType57; + const $$createField67_0 = $$createType23; + const $$createField68_0 = $$createType24; + const $$createField69_0 = $$createType24; + const $$createField70_0 = $$createType58; + const $$createField71_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField72_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField73_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField74_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField75_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField76_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField77_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField78_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField79_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField80_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField81_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField82_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField83_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField84_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField85_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField86_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField87_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField88_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField89_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField90_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField91_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField92_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField93_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField94_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField95_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField96_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField97_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField98_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField99_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField100_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField101_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField102_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField103_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField104_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField105_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField106_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField107_0 = $$createType1; + const $$createField108_0 = $$createType15; + const $$createField109_0 = $$createType17; + const $$createField110_0 = $$createType8; + const $$createField111_0 = $$createType16; + const $$createField112_0 = $$createType18; + const $$createField113_0 = $$createType1; + const $$createField114_0 = $$createType7; + const $$createField115_0 = $$createType8; + const $$createField116_0 = $$createType77; + const $$createField117_0 = $$createType78; + const $$createField118_0 = $$createType15; + const $$createField119_0 = $$createType16; + const $$createField120_0 = $$createType15; + const $$createField121_0 = $$createType18; + const $$createField122_0 = $$createType16; + const $$createField123_0 = $$createType53; + const $$createField124_0 = $$createType15; + const $$createField125_0 = $$createType16; + const $$createField126_0 = $$createType17; + const $$createField127_0 = $$createType18; + const $$createField128_0 = $$createType16; + const $$createField129_0 = $$createType18; + const $$createField130_0 = $$createType2; + const $$createField131_0 = $$createType42; + const $$createField132_0 = $$createType15; + const $$createField133_0 = $$createType79; + const $$createField134_0 = $$createType16; + const $$createField135_0 = $$createType23; + const $$createField136_0 = $$createType15; + const $$createField137_0 = $$createType18; + const $$createField138_0 = $$createType24; + const $$createField139_0 = $$createType16; + const $$createField140_0 = $$createType53; + const $$createField141_0 = $$createType16; + const $$createField142_0 = $$createType18; + const $$createField143_0 = $$createType48; + const $$createField144_0 = $$createType53; + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Bool" in $$parsedSource) { + $$parsedSource["Bool"] = $$createField0_0($$parsedSource["Bool"]); + } + if ("Int" in $$parsedSource) { + $$parsedSource["Int"] = $$createField1_0($$parsedSource["Int"]); + } + if ("Uint" in $$parsedSource) { + $$parsedSource["Uint"] = $$createField2_0($$parsedSource["Uint"]); + } + if ("Float" in $$parsedSource) { + $$parsedSource["Float"] = $$createField3_0($$parsedSource["Float"]); + } + if ("Complex" in $$parsedSource) { + $$parsedSource["Complex"] = $$createField4_0($$parsedSource["Complex"]); + } + if ("Byte" in $$parsedSource) { + $$parsedSource["Byte"] = $$createField5_0($$parsedSource["Byte"]); + } + if ("Rune" in $$parsedSource) { + $$parsedSource["Rune"] = $$createField6_0($$parsedSource["Rune"]); + } + if ("String" in $$parsedSource) { + $$parsedSource["String"] = $$createField7_0($$parsedSource["String"]); + } + if ("IntPtr" in $$parsedSource) { + $$parsedSource["IntPtr"] = $$createField8_0($$parsedSource["IntPtr"]); + } + if ("UintPtr" in $$parsedSource) { + $$parsedSource["UintPtr"] = $$createField9_0($$parsedSource["UintPtr"]); + } + if ("FloatPtr" in $$parsedSource) { + $$parsedSource["FloatPtr"] = $$createField10_0($$parsedSource["FloatPtr"]); + } + if ("ComplexPtr" in $$parsedSource) { + $$parsedSource["ComplexPtr"] = $$createField11_0($$parsedSource["ComplexPtr"]); + } + if ("StringPtr" in $$parsedSource) { + $$parsedSource["StringPtr"] = $$createField12_0($$parsedSource["StringPtr"]); + } + if ("NTM" in $$parsedSource) { + $$parsedSource["NTM"] = $$createField13_0($$parsedSource["NTM"]); + } + if ("NTMPtr" in $$parsedSource) { + $$parsedSource["NTMPtr"] = $$createField14_0($$parsedSource["NTMPtr"]); + } + if ("VTM" in $$parsedSource) { + $$parsedSource["VTM"] = $$createField15_0($$parsedSource["VTM"]); + } + if ("VTMPtr" in $$parsedSource) { + $$parsedSource["VTMPtr"] = $$createField16_0($$parsedSource["VTMPtr"]); + } + if ("PTM" in $$parsedSource) { + $$parsedSource["PTM"] = $$createField17_0($$parsedSource["PTM"]); + } + if ("PTMPtr" in $$parsedSource) { + $$parsedSource["PTMPtr"] = $$createField18_0($$parsedSource["PTMPtr"]); + } + if ("JTM" in $$parsedSource) { + $$parsedSource["JTM"] = $$createField19_0($$parsedSource["JTM"]); + } + if ("JTMPtr" in $$parsedSource) { + $$parsedSource["JTMPtr"] = $$createField20_0($$parsedSource["JTMPtr"]); + } + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField21_0($$parsedSource["A"]); + } + if ("APtr" in $$parsedSource) { + $$parsedSource["APtr"] = $$createField22_0($$parsedSource["APtr"]); + } + if ("TM" in $$parsedSource) { + $$parsedSource["TM"] = $$createField23_0($$parsedSource["TM"]); + } + if ("TMPtr" in $$parsedSource) { + $$parsedSource["TMPtr"] = $$createField24_0($$parsedSource["TMPtr"]); + } + if ("CI" in $$parsedSource) { + $$parsedSource["CI"] = $$createField25_0($$parsedSource["CI"]); + } + if ("CIPtr" in $$parsedSource) { + $$parsedSource["CIPtr"] = $$createField26_0($$parsedSource["CIPtr"]); + } + if ("EI" in $$parsedSource) { + $$parsedSource["EI"] = $$createField27_0($$parsedSource["EI"]); + } + if ("EIPtr" in $$parsedSource) { + $$parsedSource["EIPtr"] = $$createField28_0($$parsedSource["EIPtr"]); + } + if ("EV" in $$parsedSource) { + $$parsedSource["EV"] = $$createField29_0($$parsedSource["EV"]); + } + if ("EVPtr" in $$parsedSource) { + $$parsedSource["EVPtr"] = $$createField30_0($$parsedSource["EVPtr"]); + } + if ("EVP" in $$parsedSource) { + $$parsedSource["EVP"] = $$createField31_0($$parsedSource["EVP"]); + } + if ("EVPPtr" in $$parsedSource) { + $$parsedSource["EVPPtr"] = $$createField32_0($$parsedSource["EVPPtr"]); + } + if ("EP" in $$parsedSource) { + $$parsedSource["EP"] = $$createField33_0($$parsedSource["EP"]); + } + if ("EPPtr" in $$parsedSource) { + $$parsedSource["EPPtr"] = $$createField34_0($$parsedSource["EPPtr"]); + } + if ("EPP" in $$parsedSource) { + $$parsedSource["EPP"] = $$createField35_0($$parsedSource["EPP"]); + } + if ("EPPPtr" in $$parsedSource) { + $$parsedSource["EPPPtr"] = $$createField36_0($$parsedSource["EPPPtr"]); + } + if ("ECI" in $$parsedSource) { + $$parsedSource["ECI"] = $$createField37_0($$parsedSource["ECI"]); + } + if ("ECIPtr" in $$parsedSource) { + $$parsedSource["ECIPtr"] = $$createField38_0($$parsedSource["ECIPtr"]); + } + if ("EOI" in $$parsedSource) { + $$parsedSource["EOI"] = $$createField39_0($$parsedSource["EOI"]); + } + if ("EOIPtr" in $$parsedSource) { + $$parsedSource["EOIPtr"] = $$createField40_0($$parsedSource["EOIPtr"]); + } + if ("WT" in $$parsedSource) { + $$parsedSource["WT"] = $$createField41_0($$parsedSource["WT"]); + } + if ("WA" in $$parsedSource) { + $$parsedSource["WA"] = $$createField42_0($$parsedSource["WA"]); + } + if ("ST" in $$parsedSource) { + $$parsedSource["ST"] = $$createField43_0($$parsedSource["ST"]); + } + if ("SA" in $$parsedSource) { + $$parsedSource["SA"] = $$createField44_0($$parsedSource["SA"]); + } + if ("IntT" in $$parsedSource) { + $$parsedSource["IntT"] = $$createField45_0($$parsedSource["IntT"]); + } + if ("IntA" in $$parsedSource) { + $$parsedSource["IntA"] = $$createField46_0($$parsedSource["IntA"]); + } + if ("VT" in $$parsedSource) { + $$parsedSource["VT"] = $$createField47_0($$parsedSource["VT"]); + } + if ("VTPtr" in $$parsedSource) { + $$parsedSource["VTPtr"] = $$createField48_0($$parsedSource["VTPtr"]); + } + if ("VPT" in $$parsedSource) { + $$parsedSource["VPT"] = $$createField49_0($$parsedSource["VPT"]); + } + if ("VPTPtr" in $$parsedSource) { + $$parsedSource["VPTPtr"] = $$createField50_0($$parsedSource["VPTPtr"]); + } + if ("VA" in $$parsedSource) { + $$parsedSource["VA"] = $$createField51_0($$parsedSource["VA"]); + } + if ("VAPtr" in $$parsedSource) { + $$parsedSource["VAPtr"] = $$createField52_0($$parsedSource["VAPtr"]); + } + if ("VPA" in $$parsedSource) { + $$parsedSource["VPA"] = $$createField53_0($$parsedSource["VPA"]); + } + if ("VPAPtr" in $$parsedSource) { + $$parsedSource["VPAPtr"] = $$createField54_0($$parsedSource["VPAPtr"]); + } + if ("PT" in $$parsedSource) { + $$parsedSource["PT"] = $$createField55_0($$parsedSource["PT"]); + } + if ("PTPtr" in $$parsedSource) { + $$parsedSource["PTPtr"] = $$createField56_0($$parsedSource["PTPtr"]); + } + if ("PPT" in $$parsedSource) { + $$parsedSource["PPT"] = $$createField57_0($$parsedSource["PPT"]); + } + if ("PPTPtr" in $$parsedSource) { + $$parsedSource["PPTPtr"] = $$createField58_0($$parsedSource["PPTPtr"]); + } + if ("PA" in $$parsedSource) { + $$parsedSource["PA"] = $$createField59_0($$parsedSource["PA"]); + } + if ("PAPtr" in $$parsedSource) { + $$parsedSource["PAPtr"] = $$createField60_0($$parsedSource["PAPtr"]); + } + if ("PPA" in $$parsedSource) { + $$parsedSource["PPA"] = $$createField61_0($$parsedSource["PPA"]); + } + if ("PPAPtr" in $$parsedSource) { + $$parsedSource["PPAPtr"] = $$createField62_0($$parsedSource["PPAPtr"]); + } + if ("IT" in $$parsedSource) { + $$parsedSource["IT"] = $$createField63_0($$parsedSource["IT"]); + } + if ("ITPtr" in $$parsedSource) { + $$parsedSource["ITPtr"] = $$createField64_0($$parsedSource["ITPtr"]); + } + if ("IPT" in $$parsedSource) { + $$parsedSource["IPT"] = $$createField65_0($$parsedSource["IPT"]); + } + if ("IPTPtr" in $$parsedSource) { + $$parsedSource["IPTPtr"] = $$createField66_0($$parsedSource["IPTPtr"]); + } + if ("IA" in $$parsedSource) { + $$parsedSource["IA"] = $$createField67_0($$parsedSource["IA"]); + } + if ("IAPtr" in $$parsedSource) { + $$parsedSource["IAPtr"] = $$createField68_0($$parsedSource["IAPtr"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField69_0($$parsedSource["IPA"]); + } + if ("IPAPtr" in $$parsedSource) { + $$parsedSource["IPAPtr"] = $$createField70_0($$parsedSource["IPAPtr"]); + } + if ("TPR" in $$parsedSource) { + $$parsedSource["TPR"] = $$createField71_0($$parsedSource["TPR"]); + } + if ("TPRPtr" in $$parsedSource) { + $$parsedSource["TPRPtr"] = $$createField72_0($$parsedSource["TPRPtr"]); + } + if ("TPS" in $$parsedSource) { + $$parsedSource["TPS"] = $$createField73_0($$parsedSource["TPS"]); + } + if ("TPSPtr" in $$parsedSource) { + $$parsedSource["TPSPtr"] = $$createField74_0($$parsedSource["TPSPtr"]); + } + if ("TPT" in $$parsedSource) { + $$parsedSource["TPT"] = $$createField75_0($$parsedSource["TPT"]); + } + if ("TPTPtr" in $$parsedSource) { + $$parsedSource["TPTPtr"] = $$createField76_0($$parsedSource["TPTPtr"]); + } + if ("TPU" in $$parsedSource) { + $$parsedSource["TPU"] = $$createField77_0($$parsedSource["TPU"]); + } + if ("TPUPtr" in $$parsedSource) { + $$parsedSource["TPUPtr"] = $$createField78_0($$parsedSource["TPUPtr"]); + } + if ("TPV" in $$parsedSource) { + $$parsedSource["TPV"] = $$createField79_0($$parsedSource["TPV"]); + } + if ("TPVPtr" in $$parsedSource) { + $$parsedSource["TPVPtr"] = $$createField80_0($$parsedSource["TPVPtr"]); + } + if ("TPW" in $$parsedSource) { + $$parsedSource["TPW"] = $$createField81_0($$parsedSource["TPW"]); + } + if ("TPWPtr" in $$parsedSource) { + $$parsedSource["TPWPtr"] = $$createField82_0($$parsedSource["TPWPtr"]); + } + if ("TPX" in $$parsedSource) { + $$parsedSource["TPX"] = $$createField83_0($$parsedSource["TPX"]); + } + if ("TPXPtr" in $$parsedSource) { + $$parsedSource["TPXPtr"] = $$createField84_0($$parsedSource["TPXPtr"]); + } + if ("TPY" in $$parsedSource) { + $$parsedSource["TPY"] = $$createField85_0($$parsedSource["TPY"]); + } + if ("TPYPtr" in $$parsedSource) { + $$parsedSource["TPYPtr"] = $$createField86_0($$parsedSource["TPYPtr"]); + } + if ("TPZ" in $$parsedSource) { + $$parsedSource["TPZ"] = $$createField87_0($$parsedSource["TPZ"]); + } + if ("TPZPtr" in $$parsedSource) { + $$parsedSource["TPZPtr"] = $$createField88_0($$parsedSource["TPZPtr"]); + } + if ("GAR" in $$parsedSource) { + $$parsedSource["GAR"] = $$createField89_0($$parsedSource["GAR"]); + } + if ("GARPtr" in $$parsedSource) { + $$parsedSource["GARPtr"] = $$createField90_0($$parsedSource["GARPtr"]); + } + if ("GAS" in $$parsedSource) { + $$parsedSource["GAS"] = $$createField91_0($$parsedSource["GAS"]); + } + if ("GASPtr" in $$parsedSource) { + $$parsedSource["GASPtr"] = $$createField92_0($$parsedSource["GASPtr"]); + } + if ("GAT" in $$parsedSource) { + $$parsedSource["GAT"] = $$createField93_0($$parsedSource["GAT"]); + } + if ("GATPtr" in $$parsedSource) { + $$parsedSource["GATPtr"] = $$createField94_0($$parsedSource["GATPtr"]); + } + if ("GAU" in $$parsedSource) { + $$parsedSource["GAU"] = $$createField95_0($$parsedSource["GAU"]); + } + if ("GAUPtr" in $$parsedSource) { + $$parsedSource["GAUPtr"] = $$createField96_0($$parsedSource["GAUPtr"]); + } + if ("GAV" in $$parsedSource) { + $$parsedSource["GAV"] = $$createField97_0($$parsedSource["GAV"]); + } + if ("GAVPtr" in $$parsedSource) { + $$parsedSource["GAVPtr"] = $$createField98_0($$parsedSource["GAVPtr"]); + } + if ("GAW" in $$parsedSource) { + $$parsedSource["GAW"] = $$createField99_0($$parsedSource["GAW"]); + } + if ("GAWPtr" in $$parsedSource) { + $$parsedSource["GAWPtr"] = $$createField100_0($$parsedSource["GAWPtr"]); + } + if ("GAX" in $$parsedSource) { + $$parsedSource["GAX"] = $$createField101_0($$parsedSource["GAX"]); + } + if ("GAXPtr" in $$parsedSource) { + $$parsedSource["GAXPtr"] = $$createField102_0($$parsedSource["GAXPtr"]); + } + if ("GAY" in $$parsedSource) { + $$parsedSource["GAY"] = $$createField103_0($$parsedSource["GAY"]); + } + if ("GAYPtr" in $$parsedSource) { + $$parsedSource["GAYPtr"] = $$createField104_0($$parsedSource["GAYPtr"]); + } + if ("GAZ" in $$parsedSource) { + $$parsedSource["GAZ"] = $$createField105_0($$parsedSource["GAZ"]); + } + if ("GAZPtr" in $$parsedSource) { + $$parsedSource["GAZPtr"] = $$createField106_0($$parsedSource["GAZPtr"]); + } + if ("GACi" in $$parsedSource) { + $$parsedSource["GACi"] = $$createField107_0($$parsedSource["GACi"]); + } + if ("GACV" in $$parsedSource) { + $$parsedSource["GACV"] = $$createField108_0($$parsedSource["GACV"]); + } + if ("GACP" in $$parsedSource) { + $$parsedSource["GACP"] = $$createField109_0($$parsedSource["GACP"]); + } + if ("GACiPtr" in $$parsedSource) { + $$parsedSource["GACiPtr"] = $$createField110_0($$parsedSource["GACiPtr"]); + } + if ("GACVPtr" in $$parsedSource) { + $$parsedSource["GACVPtr"] = $$createField111_0($$parsedSource["GACVPtr"]); + } + if ("GACPPtr" in $$parsedSource) { + $$parsedSource["GACPPtr"] = $$createField112_0($$parsedSource["GACPPtr"]); + } + if ("GABi" in $$parsedSource) { + $$parsedSource["GABi"] = $$createField113_0($$parsedSource["GABi"]); + } + if ("GABs" in $$parsedSource) { + $$parsedSource["GABs"] = $$createField114_0($$parsedSource["GABs"]); + } + if ("GABiPtr" in $$parsedSource) { + $$parsedSource["GABiPtr"] = $$createField115_0($$parsedSource["GABiPtr"]); + } + if ("GABT" in $$parsedSource) { + $$parsedSource["GABT"] = $$createField116_0($$parsedSource["GABT"]); + } + if ("GABTPtr" in $$parsedSource) { + $$parsedSource["GABTPtr"] = $$createField117_0($$parsedSource["GABTPtr"]); + } + if ("GAGT" in $$parsedSource) { + $$parsedSource["GAGT"] = $$createField118_0($$parsedSource["GAGT"]); + } + if ("GAGTPtr" in $$parsedSource) { + $$parsedSource["GAGTPtr"] = $$createField119_0($$parsedSource["GAGTPtr"]); + } + if ("GANBV" in $$parsedSource) { + $$parsedSource["GANBV"] = $$createField120_0($$parsedSource["GANBV"]); + } + if ("GANBP" in $$parsedSource) { + $$parsedSource["GANBP"] = $$createField121_0($$parsedSource["GANBP"]); + } + if ("GANBVPtr" in $$parsedSource) { + $$parsedSource["GANBVPtr"] = $$createField122_0($$parsedSource["GANBVPtr"]); + } + if ("GANBPPtr" in $$parsedSource) { + $$parsedSource["GANBPPtr"] = $$createField123_0($$parsedSource["GANBPPtr"]); + } + if ("GAPlV1" in $$parsedSource) { + $$parsedSource["GAPlV1"] = $$createField124_0($$parsedSource["GAPlV1"]); + } + if ("GAPlV2" in $$parsedSource) { + $$parsedSource["GAPlV2"] = $$createField125_0($$parsedSource["GAPlV2"]); + } + if ("GAPlP1" in $$parsedSource) { + $$parsedSource["GAPlP1"] = $$createField126_0($$parsedSource["GAPlP1"]); + } + if ("GAPlP2" in $$parsedSource) { + $$parsedSource["GAPlP2"] = $$createField127_0($$parsedSource["GAPlP2"]); + } + if ("GAPlVPtr" in $$parsedSource) { + $$parsedSource["GAPlVPtr"] = $$createField128_0($$parsedSource["GAPlVPtr"]); + } + if ("GAPlPPtr" in $$parsedSource) { + $$parsedSource["GAPlPPtr"] = $$createField129_0($$parsedSource["GAPlPPtr"]); + } + if ("GAMi" in $$parsedSource) { + $$parsedSource["GAMi"] = $$createField130_0($$parsedSource["GAMi"]); + } + if ("GAMS" in $$parsedSource) { + $$parsedSource["GAMS"] = $$createField131_0($$parsedSource["GAMS"]); + } + if ("GAMV" in $$parsedSource) { + $$parsedSource["GAMV"] = $$createField132_0($$parsedSource["GAMV"]); + } + if ("GAMSPtr" in $$parsedSource) { + $$parsedSource["GAMSPtr"] = $$createField133_0($$parsedSource["GAMSPtr"]); + } + if ("GAMVPtr" in $$parsedSource) { + $$parsedSource["GAMVPtr"] = $$createField134_0($$parsedSource["GAMVPtr"]); + } + if ("GAII" in $$parsedSource) { + $$parsedSource["GAII"] = $$createField135_0($$parsedSource["GAII"]); + } + if ("GAIV" in $$parsedSource) { + $$parsedSource["GAIV"] = $$createField136_0($$parsedSource["GAIV"]); + } + if ("GAIP" in $$parsedSource) { + $$parsedSource["GAIP"] = $$createField137_0($$parsedSource["GAIP"]); + } + if ("GAIIPtr" in $$parsedSource) { + $$parsedSource["GAIIPtr"] = $$createField138_0($$parsedSource["GAIIPtr"]); + } + if ("GAIVPtr" in $$parsedSource) { + $$parsedSource["GAIVPtr"] = $$createField139_0($$parsedSource["GAIVPtr"]); + } + if ("GAIPPtr" in $$parsedSource) { + $$parsedSource["GAIPPtr"] = $$createField140_0($$parsedSource["GAIPPtr"]); + } + if ("GAPrV" in $$parsedSource) { + $$parsedSource["GAPrV"] = $$createField141_0($$parsedSource["GAPrV"]); + } + if ("GAPrP" in $$parsedSource) { + $$parsedSource["GAPrP"] = $$createField142_0($$parsedSource["GAPrP"]); + } + if ("GAPrVPtr" in $$parsedSource) { + $$parsedSource["GAPrVPtr"] = $$createField143_0($$parsedSource["GAPrVPtr"]); + } + if ("GAPrPPtr" in $$parsedSource) { + $$parsedSource["GAPrPPtr"] = $$createField144_0($$parsedSource["GAPrPPtr"]); + } + return new Maps(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +/** + * @template X + * @typedef {X} MixedCstrAlias + */ + +/** + * @template V + * @typedef {V} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {W} PointableCstrAlias + */ + +/** + * @typedef {PointerTextMarshaler} PointerAlias + */ + +/** + * @typedef {string} PointerTextMarshaler + */ + +/** + * @typedef {string} StringAlias + */ + +/** + * @typedef {string} StringType + */ + +/** + * @typedef {ValueTextMarshaler} ValueAlias + */ + +/** + * @typedef {string} ValueTextMarshaler + */ + +// Private type creation functions +const $$createType0 = $Create.Map($Create.Any, $Create.Any); +const $$createType1 = $Create.Map($Create.Any, $Create.Any); +const $$createType2 = $Create.Map($Create.Any, $Create.Any); +const $$createType3 = $Create.Map($Create.Any, $Create.Any); +const $$createType4 = $Create.Map($Create.Any, $Create.Any); +const $$createType5 = $Create.Map($Create.Any, $Create.Any); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Map($Create.Any, $Create.Any); +const $$createType8 = $Create.Map($Create.Any, $Create.Any); +const $$createType9 = $Create.Map($Create.Any, $Create.Any); +const $$createType10 = $Create.Map($Create.Any, $Create.Any); +const $$createType11 = $Create.Map($Create.Any, $Create.Any); +const $$createType12 = $Create.Map($Create.Any, $Create.Any); +const $$createType13 = $Create.Map($Create.Any, $Create.Any); +const $$createType14 = $Create.Map($Create.Any, $Create.Any); +const $$createType15 = $Create.Map($Create.Any, $Create.Any); +const $$createType16 = $Create.Map($Create.Any, $Create.Any); +const $$createType17 = $Create.Map($Create.Any, $Create.Any); +const $$createType18 = $Create.Map($Create.Any, $Create.Any); +const $$createType19 = $Create.Map($Create.Any, $Create.Any); +const $$createType20 = $Create.Map($Create.Any, $Create.Any); +const $$createType21 = $Create.Map($Create.Any, $Create.Any); +const $$createType22 = $Create.Map($Create.Any, $Create.Any); +const $$createType23 = $Create.Map($Create.Any, $Create.Any); +const $$createType24 = $Create.Map($Create.Any, $Create.Any); +const $$createType25 = $Create.Map($Create.Any, $Create.Any); +const $$createType26 = $Create.Map($Create.Any, $Create.Any); +const $$createType27 = $Create.Map($Create.Any, $Create.Any); +const $$createType28 = $Create.Map($Create.Any, $Create.Any); +const $$createType29 = $Create.Map($Create.Any, $Create.Any); +const $$createType30 = $Create.Map($Create.Any, $Create.Any); +const $$createType31 = $Create.Map($Create.Any, $Create.Any); +const $$createType32 = $Create.Map($Create.Any, $Create.Any); +const $$createType33 = $Create.Map($Create.Any, $Create.Any); +const $$createType34 = $Create.Map($Create.Any, $Create.Any); +const $$createType35 = $Create.Map($Create.Any, $Create.Any); +const $$createType36 = $Create.Map($Create.Any, $Create.Any); +const $$createType37 = $Create.Map($Create.Any, $Create.Any); +const $$createType38 = $Create.Map($Create.Any, $Create.Any); +const $$createType39 = $Create.Map($Create.Any, $Create.Any); +const $$createType40 = $Create.Map($Create.Any, $Create.Any); +const $$createType41 = $Create.Map($Create.Any, $Create.Any); +const $$createType42 = $Create.Map($Create.Any, $Create.Any); +const $$createType43 = $Create.Map($Create.Any, $Create.Any); +const $$createType44 = $Create.Map($Create.Any, $Create.Any); +const $$createType45 = $Create.Map($Create.Any, $Create.Any); +const $$createType46 = $Create.Map($Create.Any, $Create.Any); +const $$createType47 = $Create.Map($Create.Any, $Create.Any); +const $$createType48 = $Create.Map($Create.Any, $Create.Any); +const $$createType49 = $Create.Map($Create.Any, $Create.Any); +const $$createType50 = $Create.Map($Create.Any, $Create.Any); +const $$createType51 = $Create.Map($Create.Any, $Create.Any); +const $$createType52 = $Create.Map($Create.Any, $Create.Any); +const $$createType53 = $Create.Map($Create.Any, $Create.Any); +const $$createType54 = $Create.Map($Create.Any, $Create.Any); +const $$createType55 = $Create.Map($Create.Any, $Create.Any); +const $$createType56 = $Create.Map($Create.Any, $Create.Any); +const $$createType57 = $Create.Map($Create.Any, $Create.Any); +const $$createType58 = $Create.Map($Create.Any, $Create.Any); +const $$createType59 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType60 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType61 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType62 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType63 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType64 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType65 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType66 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType67 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType68 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType69 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType70 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType71 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType72 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType73 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType74 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType75 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType76 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType77 = $Create.Map($Create.Any, $Create.Any); +const $$createType78 = $Create.Map($Create.Any, $Create.Any); +const $$createType79 = $Create.Map($Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js new file mode 100644 index 000000000..870b2804b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js @@ -0,0 +1,23 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>>} + */ +export function Method() { + return $Call.ByID(4021345184).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Maps.createFrom($Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js new file mode 100644 index 000000000..8f525252e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js @@ -0,0 +1,116 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Data, + ImplicitNonMarshaler, + NonMarshaler +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * any + * @typedef {$models.AliasJsonMarshaler} AliasJsonMarshaler + */ + +/** + * any + * @typedef {$models.AliasMarshaler} AliasMarshaler + */ + +/** + * struct{} + * @typedef {$models.AliasNonMarshaler} AliasNonMarshaler + */ + +/** + * string + * @typedef {$models.AliasTextMarshaler} AliasTextMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitJsonButText} ImplicitJsonButText + */ + +/** + * any + * @typedef {$models.ImplicitJsonMarshaler} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitMarshaler} ImplicitMarshaler + */ + +/** + * string + * @typedef {$models.ImplicitNonJson} ImplicitNonJson + */ + +/** + * any + * @typedef {$models.ImplicitNonText} ImplicitNonText + */ + +/** + * any + * @typedef {$models.ImplicitTextButJson} ImplicitTextButJson + */ + +/** + * string + * @typedef {$models.ImplicitTextMarshaler} ImplicitTextMarshaler + */ + +/** + * any + * @typedef {$models.PointerJsonMarshaler} PointerJsonMarshaler + */ + +/** + * any + * @typedef {$models.PointerMarshaler} PointerMarshaler + */ + +/** + * string + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingJsonMarshaler} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingMarshaler} UnderlyingMarshaler + */ + +/** + * string + * @typedef {$models.UnderlyingTextMarshaler} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {$models.ValueJsonMarshaler} ValueJsonMarshaler + */ + +/** + * any + * @typedef {$models.ValueMarshaler} ValueMarshaler + */ + +/** + * string + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js new file mode 100644 index 000000000..77fe552ea --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js @@ -0,0 +1,630 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + * @typedef {any} AliasJsonMarshaler + */ + +/** + * any + * @typedef {any} AliasMarshaler + */ + +/** + * struct{} + * @typedef { { + * } } AliasNonMarshaler + */ + +/** + * string + * @typedef {string} AliasTextMarshaler + */ + +export class Data { + /** + * Creates a new Data instance. + * @param {Partial} [$$source = {}] - The source object to create the Data. + */ + constructor($$source = {}) { + if (!("NM" in $$source)) { + /** + * @member + * @type {NonMarshaler} + */ + this["NM"] = (new NonMarshaler()); + } + if (!("NMPtr" in $$source)) { + /** + * NonMarshaler | null + * @member + * @type {NonMarshaler | null} + */ + this["NMPtr"] = null; + } + if (!("VJM" in $$source)) { + /** + * @member + * @type {ValueJsonMarshaler} + */ + this["VJM"] = null; + } + if (!("VJMPtr" in $$source)) { + /** + * ValueJsonMarshaler | null + * @member + * @type {ValueJsonMarshaler | null} + */ + this["VJMPtr"] = null; + } + if (!("PJM" in $$source)) { + /** + * @member + * @type {PointerJsonMarshaler} + */ + this["PJM"] = null; + } + if (!("PJMPtr" in $$source)) { + /** + * PointerJsonMarshaler | null + * @member + * @type {PointerJsonMarshaler | null} + */ + this["PJMPtr"] = null; + } + if (!("VTM" in $$source)) { + /** + * @member + * @type {ValueTextMarshaler} + */ + this["VTM"] = ""; + } + if (!("VTMPtr" in $$source)) { + /** + * ValueTextMarshaler | null + * @member + * @type {ValueTextMarshaler | null} + */ + this["VTMPtr"] = null; + } + if (!("PTM" in $$source)) { + /** + * @member + * @type {PointerTextMarshaler} + */ + this["PTM"] = ""; + } + if (!("PTMPtr" in $$source)) { + /** + * PointerTextMarshaler | null + * @member + * @type {PointerTextMarshaler | null} + */ + this["PTMPtr"] = null; + } + if (!("VM" in $$source)) { + /** + * @member + * @type {ValueMarshaler} + */ + this["VM"] = null; + } + if (!("VMPtr" in $$source)) { + /** + * ValueMarshaler | null + * @member + * @type {ValueMarshaler | null} + */ + this["VMPtr"] = null; + } + if (!("PM" in $$source)) { + /** + * @member + * @type {PointerMarshaler} + */ + this["PM"] = null; + } + if (!("PMPtr" in $$source)) { + /** + * PointerMarshaler | null + * @member + * @type {PointerMarshaler | null} + */ + this["PMPtr"] = null; + } + if (!("UJM" in $$source)) { + /** + * @member + * @type {UnderlyingJsonMarshaler} + */ + this["UJM"] = null; + } + if (!("UJMPtr" in $$source)) { + /** + * UnderlyingJsonMarshaler | null + * @member + * @type {UnderlyingJsonMarshaler | null} + */ + this["UJMPtr"] = null; + } + if (!("UTM" in $$source)) { + /** + * @member + * @type {UnderlyingTextMarshaler} + */ + this["UTM"] = ""; + } + if (!("UTMPtr" in $$source)) { + /** + * UnderlyingTextMarshaler | null + * @member + * @type {UnderlyingTextMarshaler | null} + */ + this["UTMPtr"] = null; + } + if (!("UM" in $$source)) { + /** + * @member + * @type {UnderlyingMarshaler} + */ + this["UM"] = null; + } + if (!("UMPtr" in $$source)) { + /** + * UnderlyingMarshaler | null + * @member + * @type {UnderlyingMarshaler | null} + */ + this["UMPtr"] = null; + } + if (!("JM" in $$source)) { + /** + * any + * @member + * @type {any} + */ + this["JM"] = null; + } + if (!("JMPtr" in $$source)) { + /** + * any | null + * @member + * @type {any | null} + */ + this["JMPtr"] = null; + } + if (!("TM" in $$source)) { + /** + * string + * @member + * @type {string} + */ + this["TM"] = ""; + } + if (!("TMPtr" in $$source)) { + /** + * string | null + * @member + * @type {string | null} + */ + this["TMPtr"] = null; + } + if (!("CJM" in $$source)) { + /** + * any + * @member + * @type {any} + */ + this["CJM"] = null; + } + if (!("CJMPtr" in $$source)) { + /** + * any | null + * @member + * @type {any | null} + */ + this["CJMPtr"] = null; + } + if (!("CTM" in $$source)) { + /** + * string + * @member + * @type {string} + */ + this["CTM"] = ""; + } + if (!("CTMPtr" in $$source)) { + /** + * string | null + * @member + * @type {string | null} + */ + this["CTMPtr"] = null; + } + if (!("CM" in $$source)) { + /** + * any + * @member + * @type {any} + */ + this["CM"] = null; + } + if (!("CMPtr" in $$source)) { + /** + * any | null + * @member + * @type {any | null} + */ + this["CMPtr"] = null; + } + if (!("ANM" in $$source)) { + /** + * @member + * @type {AliasNonMarshaler} + */ + this["ANM"] = {}; + } + if (!("ANMPtr" in $$source)) { + /** + * AliasNonMarshaler | null + * @member + * @type {AliasNonMarshaler | null} + */ + this["ANMPtr"] = null; + } + if (!("AJM" in $$source)) { + /** + * @member + * @type {AliasJsonMarshaler} + */ + this["AJM"] = null; + } + if (!("AJMPtr" in $$source)) { + /** + * AliasJsonMarshaler | null + * @member + * @type {AliasJsonMarshaler | null} + */ + this["AJMPtr"] = null; + } + if (!("ATM" in $$source)) { + /** + * @member + * @type {AliasTextMarshaler} + */ + this["ATM"] = ""; + } + if (!("ATMPtr" in $$source)) { + /** + * AliasTextMarshaler | null + * @member + * @type {AliasTextMarshaler | null} + */ + this["ATMPtr"] = null; + } + if (!("AM" in $$source)) { + /** + * @member + * @type {AliasMarshaler} + */ + this["AM"] = null; + } + if (!("AMPtr" in $$source)) { + /** + * AliasMarshaler | null + * @member + * @type {AliasMarshaler | null} + */ + this["AMPtr"] = null; + } + if (!("ImJM" in $$source)) { + /** + * @member + * @type {ImplicitJsonMarshaler} + */ + this["ImJM"] = null; + } + if (!("ImJMPtr" in $$source)) { + /** + * ImplicitJsonMarshaler | null + * @member + * @type {ImplicitJsonMarshaler | null} + */ + this["ImJMPtr"] = null; + } + if (!("ImTM" in $$source)) { + /** + * @member + * @type {ImplicitTextMarshaler} + */ + this["ImTM"] = ""; + } + if (!("ImTMPtr" in $$source)) { + /** + * ImplicitTextMarshaler | null + * @member + * @type {ImplicitTextMarshaler | null} + */ + this["ImTMPtr"] = null; + } + if (!("ImM" in $$source)) { + /** + * @member + * @type {ImplicitMarshaler} + */ + this["ImM"] = null; + } + if (!("ImMPtr" in $$source)) { + /** + * ImplicitMarshaler | null + * @member + * @type {ImplicitMarshaler | null} + */ + this["ImMPtr"] = null; + } + if (!("ImNJ" in $$source)) { + /** + * @member + * @type {ImplicitNonJson} + */ + this["ImNJ"] = ""; + } + if (!("ImNJPtr" in $$source)) { + /** + * ImplicitNonJson | null + * @member + * @type {ImplicitNonJson | null} + */ + this["ImNJPtr"] = null; + } + if (!("ImNT" in $$source)) { + /** + * @member + * @type {ImplicitNonText} + */ + this["ImNT"] = null; + } + if (!("ImNTPtr" in $$source)) { + /** + * ImplicitNonText | null + * @member + * @type {ImplicitNonText | null} + */ + this["ImNTPtr"] = null; + } + if (!("ImNM" in $$source)) { + /** + * @member + * @type {ImplicitNonMarshaler} + */ + this["ImNM"] = (new ImplicitNonMarshaler()); + } + if (!("ImNMPtr" in $$source)) { + /** + * ImplicitNonMarshaler | null + * @member + * @type {ImplicitNonMarshaler | null} + */ + this["ImNMPtr"] = null; + } + if (!("ImJbT" in $$source)) { + /** + * @member + * @type {ImplicitJsonButText} + */ + this["ImJbT"] = null; + } + if (!("ImJbTPtr" in $$source)) { + /** + * ImplicitJsonButText | null + * @member + * @type {ImplicitJsonButText | null} + */ + this["ImJbTPtr"] = null; + } + if (!("ImTbJ" in $$source)) { + /** + * @member + * @type {ImplicitTextButJson} + */ + this["ImTbJ"] = null; + } + if (!("ImTbJPtr" in $$source)) { + /** + * ImplicitTextButJson | null + * @member + * @type {ImplicitTextButJson | null} + */ + this["ImTbJPtr"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Data instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Data} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField48_0 = $$createType2; + const $$createField49_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("NM" in $$parsedSource) { + $$parsedSource["NM"] = $$createField0_0($$parsedSource["NM"]); + } + if ("NMPtr" in $$parsedSource) { + $$parsedSource["NMPtr"] = $$createField1_0($$parsedSource["NMPtr"]); + } + if ("ImNM" in $$parsedSource) { + $$parsedSource["ImNM"] = $$createField48_0($$parsedSource["ImNM"]); + } + if ("ImNMPtr" in $$parsedSource) { + $$parsedSource["ImNMPtr"] = $$createField49_0($$parsedSource["ImNMPtr"]); + } + return new Data(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * any + * @typedef {any} ImplicitJsonButText + */ + +/** + * any + * @typedef {any} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {any} ImplicitMarshaler + */ + +/** + * string + * @typedef {string} ImplicitNonJson + */ + +/** + * class{ Marshaler, TextMarshaler } + */ +export class ImplicitNonMarshaler { + /** + * Creates a new ImplicitNonMarshaler instance. + * @param {Partial} [$$source = {}] - The source object to create the ImplicitNonMarshaler. + */ + constructor($$source = {}) { + if (!("Marshaler" in $$source)) { + /** + * @member + * @type {json$0.Marshaler} + */ + this["Marshaler"] = null; + } + if (!("TextMarshaler" in $$source)) { + /** + * @member + * @type {encoding$0.TextMarshaler} + */ + this["TextMarshaler"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new ImplicitNonMarshaler instance from a string or object. + * @param {any} [$$source = {}] + * @returns {ImplicitNonMarshaler} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new ImplicitNonMarshaler(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * any + * @typedef {any} ImplicitNonText + */ + +/** + * any + * @typedef {any} ImplicitTextButJson + */ + +/** + * string + * @typedef {string} ImplicitTextMarshaler + */ + +/** + * class {} + */ +export class NonMarshaler { + /** + * Creates a new NonMarshaler instance. + * @param {Partial} [$$source = {}] - The source object to create the NonMarshaler. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new NonMarshaler instance from a string or object. + * @param {any} [$$source = {}] + * @returns {NonMarshaler} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new NonMarshaler(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * any + * @typedef {any} PointerJsonMarshaler + */ + +/** + * any + * @typedef {any} PointerMarshaler + */ + +/** + * string + * @typedef {string} PointerTextMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingMarshaler + */ + +/** + * string + * @typedef {string} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {any} ValueJsonMarshaler + */ + +/** + * any + * @typedef {any} ValueMarshaler + */ + +/** + * string + * @typedef {string} ValueTextMarshaler + */ + +// Private type creation functions +const $$createType0 = NonMarshaler.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = ImplicitNonMarshaler.createFrom; +const $$createType3 = $Create.Nullable($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js new file mode 100644 index 000000000..5b415f83e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js @@ -0,0 +1,23 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Data>} + */ +export function Method() { + return $Call.ByID(4021345184).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Data.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js new file mode 100644 index 000000000..9be766c03 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +export { + HowDifferent, + Impersonator, + Person, + PrivatePerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js new file mode 100644 index 000000000..b42e223fa --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js @@ -0,0 +1,181 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + * @template How + */ +export class HowDifferent { + /** + * Creates a new HowDifferent instance. + * @param {Partial>} [$$source = {}] - The source object to create the HowDifferent. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * They have a name as well. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + /** + * But they may have many differences. + * @member + * @type {{ [_: string]: How }[]} + */ + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class HowDifferent. + * @template [How=any] + * @param {(source: any) => How} $$createParamHow + * @returns {($$source?: any) => HowDifferent} + */ + static createFrom($$createParamHow) { + const $$createField1_0 = $$createType1($$createParamHow); + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new HowDifferent(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +/** + * Impersonator gets their fields from other people. + */ +export const Impersonator = other$0.OtherPerson; + +/** + * Impersonator gets their fields from other people. + * @typedef {other$0.OtherPerson} Impersonator + */ + +/** + * Person is not a number. + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * They have a name. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + /** + * Exactly 4 sketchy friends. + * @member + * @type {Impersonator[]} + */ + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField1_0($$parsedSource["Friends"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +export class personImpl { + /** + * Creates a new personImpl instance. + * @param {Partial} [$$source = {}] - The source object to create the personImpl. + */ + constructor($$source = {}) { + if (!("Nickname" in $$source)) { + /** + * Nickname conceals a person's identity. + * @member + * @type {string} + */ + this["Nickname"] = ""; + } + if (!("Name" in $$source)) { + /** + * They have a name. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + /** + * Exactly 4 sketchy friends. + * @member + * @type {Impersonator[]} + */ + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new personImpl instance from a string or object. + * @param {any} [$$source = {}] + * @returns {personImpl} + */ + static createFrom($$source = {}) { + const $$createField2_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField2_0($$parsedSource["Friends"]); + } + return new personImpl(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export const PrivatePerson = personImpl; + +/** + * PrivatePerson gets their fields from hidden sources. + * @typedef {personImpl} PrivatePerson + */ + +// Private type creation functions +const $$createType0 = /** @type {(...args: any[]) => any} */(($$createParamHow) => $Create.Map($Create.Any, $$createParamHow)); +const $$createType1 = /** @type {(...args: any[]) => any} */(($$createParamHow) => $Create.Array($$createType0($$createParamHow))); +const $$createType2 = other$0.OtherPerson.createFrom($Create.Any); +const $$createType3 = $Create.Array($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js new file mode 100644 index 000000000..db4e64147 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +export { + OtherPerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js new file mode 100644 index 000000000..89992cacf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js @@ -0,0 +1,60 @@ +// @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 "/wails/runtime.js"; + +/** + * OtherPerson is like a person, but different. + * @template T + */ +export class OtherPerson { + /** + * Creates a new OtherPerson instance. + * @param {Partial>} [$$source = {}] - The source object to create the OtherPerson. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * They have a name as well. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + /** + * But they may have many differences. + * @member + * @type {T[]} + */ + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class OtherPerson. + * @template [T=any] + * @param {(source: any) => T} $$createParamT + * @returns {($$source?: any) => OtherPerson} + */ + static createFrom($$createParamT) { + const $$createField1_0 = $$createType0($$createParamT); + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new OtherPerson(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +// Private type creation functions +const $$createType0 = /** @type {(...args: any[]) => any} */(($$createParamT) => $Create.Array($$createParamT)); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js new file mode 100644 index 000000000..36b25c183 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(3606939272); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js new file mode 100644 index 000000000..a87ccf6c4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js @@ -0,0 +1,42 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByID(2124352079).then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + })); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(4281222271); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.HowDifferent.createFrom($Create.Any); +const $$createType2 = $models.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js new file mode 100644 index 000000000..5fb44fbc6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(3566862802); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js new file mode 100644 index 000000000..5ec6c820e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js @@ -0,0 +1,42 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByID(2590614085).then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + })); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(773650321); +} + +// Private type creation functions +const $$createType0 = nobindingshere$0.Person.createFrom; +const $$createType1 = nobindingshere$0.HowDifferent.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js new file mode 100644 index 000000000..8afbd8b3a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} $0 + * @returns {$CancellablePromise} + */ +export function Greet($0) { + return $Call.ByID(1411160069, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js new file mode 100644 index 000000000..734fb02e7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js new file mode 100644 index 000000000..e50a4a6ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js new file mode 100644 index 000000000..f017774d8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js new file mode 100644 index 000000000..e50a4a6ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js new file mode 100644 index 000000000..f017774d8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js new file mode 100644 index 000000000..50737a34b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js new file mode 100644 index 000000000..04771d2ca --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js @@ -0,0 +1,54 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {services$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js new file mode 100644 index 000000000..24dc0334e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(3568225479).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js new file mode 100644 index 000000000..855602e98 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js @@ -0,0 +1,379 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByID(3862002418, $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByID(2424639793, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByID(3132595881, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByID(3306292566, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByID(1754277916, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByID(1909469092, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByID(4251088558, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByID(1343888303, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByID(2205561041, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByID(572240879, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByID(2189402897, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByID(642881729, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByID(1066151743, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByID(2718999663, $in); +} + +/** + * @param {{ [_: `${number}`]: number }} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByID(2386486356, $in); +} + +/** + * @param {{ [_: `${number}`]: number | null }} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByID(2163571325, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByID(2900172572, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] }>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByID(881980169, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByID(1075577233); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByID(3589606958, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByID(224675106, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByID(2124953624, $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByID(3516977899, $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByID(229603958, $in); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByID(3678582682, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByID(319259595, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByID(383995060, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByID(1091960237, $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByID(3835643147, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType3($result); + })); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByID(2447692557, $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByID(2943477349, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType4($result); + })); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByID(3401034892, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByID(1236957573, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByID(1160383782, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByID(1739300671, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByID(793803239, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByID(1403757716, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByID(2988345717, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByID(518250834, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByID(2836661285, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByID(1367187362, $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js new file mode 100644 index 000000000..69c96370a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js @@ -0,0 +1,57 @@ +// @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 "/wails/runtime.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + /** + * @member + * @type {Person | null} + */ + this["Parent"] = null; + } + if (!("Details" in $$source)) { + /** + * @member + * @type {{"Age": number, "Address": {"Street": string}}} + */ + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js new file mode 100644 index 000000000..855602e98 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js @@ -0,0 +1,379 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByID(3862002418, $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByID(2424639793, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByID(3132595881, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByID(3306292566, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByID(1754277916, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByID(1909469092, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByID(4251088558, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByID(1343888303, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByID(2205561041, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByID(572240879, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByID(2189402897, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByID(642881729, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByID(1066151743, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByID(2718999663, $in); +} + +/** + * @param {{ [_: `${number}`]: number }} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByID(2386486356, $in); +} + +/** + * @param {{ [_: `${number}`]: number | null }} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByID(2163571325, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByID(2900172572, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] }>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByID(881980169, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByID(1075577233); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByID(3589606958, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByID(224675106, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByID(2124953624, $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByID(3516977899, $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByID(229603958, $in); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByID(3678582682, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByID(319259595, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByID(383995060, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByID(1091960237, $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByID(3835643147, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType3($result); + })); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByID(2447692557, $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByID(2943477349, $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType4($result); + })); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByID(3401034892, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByID(1236957573, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByID(1160383782, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByID(1739300671, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByID(793803239, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByID(1403757716, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByID(2988345717, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByID(518250834, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByID(2836661285, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByID(1367187362, $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js new file mode 100644 index 000000000..69c96370a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js @@ -0,0 +1,57 @@ +// @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 "/wails/runtime.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + /** + * @member + * @type {Person | null} + */ + this["Parent"] = null; + } + if (!("Details" in $$source)) { + /** + * @member + * @type {{"Age": number, "Address": {"Street": string}}} + */ + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js new file mode 100644 index 000000000..9dfe48511 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js new file mode 100644 index 000000000..9dfe48511 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js new file mode 100644 index 000000000..50737a34b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js new file mode 100644 index 000000000..d7bfe75cf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js @@ -0,0 +1,58 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {services$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js new file mode 100644 index 000000000..de3c9d51d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(1491748400).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/warnings.log b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=false/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/index.js new file mode 100644 index 000000000..cf48d86db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/index.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {$models.TextMarshaler} TextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/json/index.js new file mode 100644 index 000000000..22f1fd904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/json/index.js @@ -0,0 +1,11 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {$models.Marshaler} Marshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/json/models.js new file mode 100644 index 000000000..96abf0ef1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/json/models.js @@ -0,0 +1,13 @@ +// @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 "/wails/runtime.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {any} Marshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/models.js new file mode 100644 index 000000000..59cfef5bd --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/encoding/models.js @@ -0,0 +1,15 @@ +// @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 "/wails/runtime.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {any} TextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js new file mode 100644 index 000000000..2352f40bc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js @@ -0,0 +1,97 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + * @param {$models.Alias} aliasValue + * @returns {$CancellablePromise<$models.Person>} + */ +export function Get(aliasValue) { + return $Call.ByName("main.GreetService.Get", aliasValue).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +/** + * Apparently, aliases are all the rage right now. + * @param {$models.AliasedPerson} p + * @returns {$CancellablePromise<$models.StrangelyAliasedPerson>} + */ +export function GetButAliased(p) { + return $Call.ByName("main.GreetService.GetButAliased", p).then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +/** + * Get someone quite different. + * @returns {$CancellablePromise<$models.GenericPerson>} + */ +export function GetButDifferent() { + return $Call.ByName("main.GreetService.GetButDifferent").then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +/** + * @returns {$CancellablePromise} + */ +export function GetButForeignPrivateAlias() { + return $Call.ByName("main.GreetService.GetButForeignPrivateAlias").then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @returns {$CancellablePromise<$models.AliasGroup>} + */ +export function GetButGenericAliases() { + return $Call.ByName("main.GreetService.GetButGenericAliases").then(/** @type {($result: any) => any} */(($result) => { + return $$createType3($result); + })); +} + +/** + * Greet a lot of unusual things. + * @param {$models.EmptyAliasStruct} $0 + * @param {$models.EmptyStruct} $1 + * @returns {$CancellablePromise<$models.AliasStruct>} + */ +export function Greet($0, $1) { + return $Call.ByName("main.GreetService.Greet", $0, $1).then(/** @type {($result: any) => any} */(($result) => { + return $$createType7($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.GenericPerson.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; +const $$createType3 = $models.AliasGroup.createFrom; +const $$createType4 = $Create.Array($Create.Any); +const $$createType5 = $Create.Array($Create.Any); +const $$createType6 = $Create.Struct({ + "NoMoreIdeas": $$createType5, +}); +const $$createType7 = $Create.Struct({ + "Foo": $$createType4, + "Other": $$createType6, +}); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js new file mode 100644 index 000000000..4278c7958 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js @@ -0,0 +1,61 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + AliasGroup, + AliasedPerson, + EmptyStruct, + GenericPerson, + GenericPersonAlias, + IndirectPersonAlias, + Person, + StrangelyAliasedPerson, + TPIndirectPersonAlias +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * A nice type Alias. + * @typedef {$models.Alias} Alias + */ + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {$models.AliasStruct} AliasStruct + */ + +/** + * An empty struct alias. + * @typedef {$models.EmptyAliasStruct} EmptyAliasStruct + */ + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {$models.GenericAlias} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {$models.GenericMapAlias} GenericMapAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {$models.GenericPtrAlias} GenericPtrAlias + */ + +/** + * Another struct alias. + * @typedef {$models.OtherAliasStruct} OtherAliasStruct + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js new file mode 100644 index 000000000..3de57786d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js @@ -0,0 +1,334 @@ +// @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 "/wails/runtime.js"; + +/** + * A nice type Alias. + * @typedef {number} Alias + */ + +/** + * A class whose fields have various aliased types. + */ +export class AliasGroup { + /** + * Creates a new AliasGroup instance. + * @param {Partial} [$$source = {}] - The source object to create the AliasGroup. + */ + constructor($$source = {}) { + if (!("GAi" in $$source)) { + /** + * @member + * @type {GenericAlias} + */ + this["GAi"] = 0; + } + if (!("GAP" in $$source)) { + /** + * @member + * @type {GenericAlias>} + */ + this["GAP"] = (new GenericPerson()); + } + if (!("GPAs" in $$source)) { + /** + * @member + * @type {GenericPtrAlias} + */ + this["GPAs"] = null; + } + if (!("GPAP" in $$source)) { + /** + * @member + * @type {GenericPtrAlias>} + */ + this["GPAP"] = null; + } + if (!("GMA" in $$source)) { + /** + * @member + * @type {GenericMapAlias} + */ + this["GMA"] = {}; + } + if (!("GPA" in $$source)) { + /** + * @member + * @type {GenericPersonAlias} + */ + this["GPA"] = (new GenericPersonAlias()); + } + if (!("IPA" in $$source)) { + /** + * @member + * @type {IndirectPersonAlias} + */ + this["IPA"] = (new IndirectPersonAlias()); + } + if (!("TPIPA" in $$source)) { + /** + * @member + * @type {TPIndirectPersonAlias} + */ + this["TPIPA"] = (new TPIndirectPersonAlias()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new AliasGroup instance from a string or object. + * @param {any} [$$source = {}] + * @returns {AliasGroup} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType0; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType5; + const $$createField4_0 = $$createType6; + const $$createField5_0 = $$createType8; + const $$createField6_0 = $$createType8; + const $$createField7_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("GAP" in $$parsedSource) { + $$parsedSource["GAP"] = $$createField1_0($$parsedSource["GAP"]); + } + if ("GPAs" in $$parsedSource) { + $$parsedSource["GPAs"] = $$createField2_0($$parsedSource["GPAs"]); + } + if ("GPAP" in $$parsedSource) { + $$parsedSource["GPAP"] = $$createField3_0($$parsedSource["GPAP"]); + } + if ("GMA" in $$parsedSource) { + $$parsedSource["GMA"] = $$createField4_0($$parsedSource["GMA"]); + } + if ("GPA" in $$parsedSource) { + $$parsedSource["GPA"] = $$createField5_0($$parsedSource["GPA"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField6_0($$parsedSource["IPA"]); + } + if ("TPIPA" in $$parsedSource) { + $$parsedSource["TPIPA"] = $$createField7_0($$parsedSource["TPIPA"]); + } + return new AliasGroup(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {Object} AliasStruct + * @property {number[]} Foo - A field with a comment. + * @property {string} [Bar] - Definitely not Foo. + * @property {string} [Baz] - Definitely not Foo. + * @property {OtherAliasStruct} Other - A nested alias struct. + */ + +/** + * An empty struct alias. + * @typedef { { + * } } EmptyAliasStruct + */ + +/** + * An empty struct. + */ +export class EmptyStruct { + /** + * Creates a new EmptyStruct instance. + * @param {Partial} [$$source = {}] - The source object to create the EmptyStruct. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new EmptyStruct instance from a string or object. + * @param {any} [$$source = {}] + * @returns {EmptyStruct} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new EmptyStruct(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {T} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {{ [_: string]: U }} GenericMapAlias + */ + +/** + * A generic struct containing an alias. + * @template T + */ +export class GenericPerson { + /** + * Creates a new GenericPerson instance. + * @param {Partial>} [$$source = {}] - The source object to create the GenericPerson. + */ + constructor($$source = {}) { + if (/** @type {any} */(false)) { + /** + * @member + * @type {T | undefined} + */ + this["Name"] = undefined; + } + if (!("AliasedField" in $$source)) { + /** + * @member + * @type {Alias} + */ + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class GenericPerson. + * @template [T=any] + * @param {(source: any) => T} $$createParamT + * @returns {($$source?: any) => GenericPerson} + */ + static createFrom($$createParamT) { + const $$createField0_0 = $$createParamT; + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Name" in $$parsedSource) { + $$parsedSource["Name"] = $$createField0_0($$parsedSource["Name"]); + } + return new GenericPerson(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +/** + * A generic alias that wraps a generic struct. + */ +export const GenericPersonAlias = GenericPerson; + +/** + * A generic alias that wraps a generic struct. + * @template T + * @typedef {GenericPerson[]>} GenericPersonAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {GenericAlias | null} GenericPtrAlias + */ + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export const IndirectPersonAlias = GenericPersonAlias; + +/** + * An alias that wraps a class through a non-typeparam alias. + * @typedef {GenericPersonAlias} IndirectPersonAlias + */ + +/** + * Another struct alias. + * @typedef {Object} OtherAliasStruct + * @property {number[]} NoMoreIdeas + */ + +/** + * A non-generic struct containing an alias. + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * The Person's name. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("AliasedField" in $$source)) { + /** + * A random alias field. + * @member + * @type {Alias} + */ + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * A class alias. + */ +export const AliasedPerson = Person; + +/** + * A class alias. + * @typedef {Person} AliasedPerson + */ + +/** + * Another class alias, but ordered after its aliased class. + */ +export const StrangelyAliasedPerson = Person; + +/** + * Another class alias, but ordered after its aliased class. + * @typedef {Person} StrangelyAliasedPerson + */ + +/** + * An alias that wraps a class through a typeparam alias. + */ +export const TPIndirectPersonAlias = GenericPerson; + +/** + * An alias that wraps a class through a typeparam alias. + * @typedef {GenericAlias>} TPIndirectPersonAlias + */ + +// Private type creation functions +const $$createType0 = GenericPerson.createFrom($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = $Create.Nullable($$createType1); +const $$createType3 = $Create.Array($Create.Any); +const $$createType4 = GenericPerson.createFrom($$createType3); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Array($Create.Any); +const $$createType8 = GenericPerson.createFrom($$createType7); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js new file mode 100644 index 000000000..f6c839720 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js new file mode 100644 index 000000000..54e794649 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service7.TestMethod"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js new file mode 100644 index 000000000..f6e8901a9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod2() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service9.TestMethod2"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js new file mode 100644 index 000000000..b5c320f3d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {$models.Person} person + * @param {$models.Embedded1} emb + * @returns {$CancellablePromise} + */ +export function Greet(person, emb) { + return $Call.ByName("main.GreetService.Greet", person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js new file mode 100644 index 000000000..ab78e5ea3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Embedded1, + Person, + Title +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Embedded3} Embedded3 + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js new file mode 100644 index 000000000..7a0edf1c7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js @@ -0,0 +1,272 @@ +// @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 "/wails/runtime.js"; + +export class Embedded1 { + /** + * Creates a new Embedded1 instance. + * @param {Partial} [$$source = {}] - The source object to create the Embedded1. + */ + constructor($$source = {}) { + if (!("Friends" in $$source)) { + /** + * Friends should be shadowed in Person by a field of lesser depth + * @member + * @type {number} + */ + this["Friends"] = 0; + } + if (!("Vanish" in $$source)) { + /** + * Vanish should be omitted from Person because there is another field with same depth and no tag + * @member + * @type {number} + */ + this["Vanish"] = 0; + } + if (!("StillThere" in $$source)) { + /** + * StillThere should be shadowed in Person by other field with same depth and a json tag + * @member + * @type {string} + */ + this["StillThere"] = ""; + } + if (!("NamingThingsIsHard" in $$source)) { + /** + * NamingThingsIsHard is a law of programming + * @member + * @type {`${boolean}`} + */ + this["NamingThingsIsHard"] = "false"; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Embedded1 instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Embedded1} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Embedded1(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * @typedef {string} Embedded3 + */ + +/** + * Person represents a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (/** @type {any} */(false)) { + /** + * Titles is optional in JSON + * @member + * @type {Title[] | undefined} + */ + this["Titles"] = undefined; + } + if (!("Names" in $$source)) { + /** + * Names has a + * multiline comment + * @member + * @type {string[]} + */ + this["Names"] = []; + } + if (!("Partner" in $$source)) { + /** + * Partner has a custom and complex JSON key + * @member + * @type {Person | null} + */ + this["Partner"] = null; + } + if (!("Friends" in $$source)) { + /** + * @member + * @type {(Person | null)[]} + */ + this["Friends"] = []; + } + if (!("NamingThingsIsHard" in $$source)) { + /** + * NamingThingsIsHard is a law of programming + * @member + * @type {`${boolean}`} + */ + this["NamingThingsIsHard"] = "false"; + } + if (!("StillThere" in $$source)) { + /** + * StillThereButRenamed should shadow in Person the other field with same depth and no json tag + * @member + * @type {Embedded3 | null} + */ + this["StillThere"] = null; + } + if (!("-" in $$source)) { + /** + * StrangeNumber maps to "-" + * @member + * @type {number} + */ + this["-"] = 0; + } + if (!("Embedded3" in $$source)) { + /** + * Embedded3 should appear with key "Embedded3" + * @member + * @type {Embedded3} + */ + this["Embedded3"] = ""; + } + if (!("StrangerNumber" in $$source)) { + /** + * StrangerNumber is serialized as a string + * @member + * @type {`${number}`} + */ + this["StrangerNumber"] = "0"; + } + if (/** @type {any} */(false)) { + /** + * StrangestString is optional and serialized as a JSON string + * @member + * @type {`"${string}"` | undefined} + */ + this["StrangestString"] = undefined; + } + if (/** @type {any} */(false)) { + /** + * StringStrangest is serialized as a JSON string and optional + * @member + * @type {`"${string}"` | undefined} + */ + this["StringStrangest"] = undefined; + } + if (/** @type {any} */(false)) { + /** + * embedded4 should be optional and appear with key "emb4" + * @member + * @type {embedded4 | undefined} + */ + this["emb4"] = undefined; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType3; + const $$createField3_0 = $$createType4; + const $$createField11_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Titles" in $$parsedSource) { + $$parsedSource["Titles"] = $$createField0_0($$parsedSource["Titles"]); + } + if ("Names" in $$parsedSource) { + $$parsedSource["Names"] = $$createField1_0($$parsedSource["Names"]); + } + if ("Partner" in $$parsedSource) { + $$parsedSource["Partner"] = $$createField2_0($$parsedSource["Partner"]); + } + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField3_0($$parsedSource["Friends"]); + } + if ("emb4" in $$parsedSource) { + $$parsedSource["emb4"] = $$createField11_0($$parsedSource["emb4"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +export class embedded4 { + /** + * Creates a new embedded4 instance. + * @param {Partial} [$$source = {}] - The source object to create the embedded4. + */ + constructor($$source = {}) { + if (!("NamingThingsIsHard" in $$source)) { + /** + * NamingThingsIsHard is a law of programming + * @member + * @type {`${boolean}`} + */ + this["NamingThingsIsHard"] = "false"; + } + if (!("Friends" in $$source)) { + /** + * Friends should not be shadowed in Person as embedded4 is not embedded + * from encoding/json's point of view; + * however, it should be shadowed in Embedded1 + * @member + * @type {boolean} + */ + this["Friends"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new embedded4 instance from a string or object. + * @param {any} [$$source = {}] + * @returns {embedded4} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new embedded4(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = Person.createFrom; +const $$createType3 = $Create.Nullable($$createType2); +const $$createType4 = $Create.Array($$createType3); +const $$createType5 = embedded4.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js new file mode 100644 index 000000000..fae9496c1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + * @param {string} str + * @param {$models.Person[]} people + * @param {{"AnotherCount": number, "AnotherOne": $models.Person | null}} $2 + * @param {{ [_: `${number}`]: boolean | null }} assoc + * @param {(number | null)[]} $4 + * @param {string[]} other + * @returns {$CancellablePromise<[$models.Person, any, number[]]>} + */ +export function Greet(str, people, $2, assoc, $4, ...other) { + return $Call.ByName("main.GreetService.Greet", str, people, $2, assoc, $4, other).then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[2] = $$createType1($result[2]); + return $result; + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Array($Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js new file mode 100644 index 000000000..82af81baf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js @@ -0,0 +1,38 @@ +// @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 "/wails/runtime.js"; + +/** + * Person represents a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js new file mode 100644 index 000000000..651fd2d97 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.StructA, $models.StructC]>} + */ +export function MakeCycles() { + return $Call.ByName("main.GreetService.MakeCycles").then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + return $result; + })); +} + +// Private type creation functions +const $$createType0 = $models.StructA.createFrom; +const $$createType1 = $models.StructC.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js new file mode 100644 index 000000000..0ad0efb4e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + StructA, + StructC, + StructE +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js new file mode 100644 index 000000000..f24f5a2c9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js @@ -0,0 +1,164 @@ +// @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 "/wails/runtime.js"; + +export class StructA { + /** + * Creates a new StructA instance. + * @param {Partial} [$$source = {}] - The source object to create the StructA. + */ + constructor($$source = {}) { + if (!("B" in $$source)) { + /** + * @member + * @type {structB | null} + */ + this["B"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructA instance from a string or object. + * @param {any} [$$source = {}] + * @returns {StructA} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("B" in $$parsedSource) { + $$parsedSource["B"] = $$createField0_0($$parsedSource["B"]); + } + return new StructA(/** @type {Partial} */($$parsedSource)); + } +} + +export class StructC { + /** + * Creates a new StructC instance. + * @param {Partial} [$$source = {}] - The source object to create the StructC. + */ + constructor($$source = {}) { + if (!("D" in $$source)) { + /** + * @member + * @type {structD} + */ + this["D"] = (new structD()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructC instance from a string or object. + * @param {any} [$$source = {}] + * @returns {StructC} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType2; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("D" in $$parsedSource) { + $$parsedSource["D"] = $$createField0_0($$parsedSource["D"]); + } + return new StructC(/** @type {Partial} */($$parsedSource)); + } +} + +export class StructE { + /** + * Creates a new StructE instance. + * @param {Partial} [$$source = {}] - The source object to create the StructE. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new StructE instance from a string or object. + * @param {any} [$$source = {}] + * @returns {StructE} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new StructE(/** @type {Partial} */($$parsedSource)); + } +} + +export class structB { + /** + * Creates a new structB instance. + * @param {Partial} [$$source = {}] - The source object to create the structB. + */ + constructor($$source = {}) { + if (!("A" in $$source)) { + /** + * @member + * @type {StructA | null} + */ + this["A"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structB instance from a string or object. + * @param {any} [$$source = {}] + * @returns {structB} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType4; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField0_0($$parsedSource["A"]); + } + return new structB(/** @type {Partial} */($$parsedSource)); + } +} + +export class structD { + /** + * Creates a new structD instance. + * @param {Partial} [$$source = {}] - The source object to create the structD. + */ + constructor($$source = {}) { + if (!("E" in $$source)) { + /** + * @member + * @type {StructE} + */ + this["E"] = (new StructE()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structD instance from a string or object. + * @param {any} [$$source = {}] + * @returns {structD} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("E" in $$parsedSource) { + $$parsedSource["E"] = $$createField0_0($$parsedSource["E"]); + } + return new structD(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = structB.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = structD.createFrom; +const $$createType3 = StructA.createFrom; +const $$createType4 = $Create.Nullable($$createType3); +const $$createType5 = StructE.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js new file mode 100644 index 000000000..b4cbaa216 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js @@ -0,0 +1,65 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]>} + */ +export function MakeCycles() { + return $Call.ByName("main.GreetService.MakeCycles").then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType9($result[1]); + return $result; + })); +} + +// Private type creation functions +var $$createType0 = /** @type {(...args: any[]) => any} */(function $$initCreateType0(...args) { + if ($$createType0 === $$initCreateType0) { + $$createType0 = $$createType3; + } + return $$createType0(...args); +}); +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = $Create.Map($Create.Any, $$createType1); +const $$createType3 = $Create.Array($$createType2); +var $$createType4 = /** @type {(...args: any[]) => any} */(function $$initCreateType4(...args) { + if ($$createType4 === $$initCreateType4) { + $$createType4 = $$createType8; + } + return $$createType4(...args); +}); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Array($Create.Any); +const $$createType7 = $Create.Struct({ + "X": $$createType5, + "Y": $$createType6, +}); +const $$createType8 = $Create.Array($$createType7); +var $$createType9 = /** @type {(...args: any[]) => any} */(function $$initCreateType9(...args) { + if ($$createType9 === $$initCreateType9) { + $$createType9 = $$createType13; + } + return $$createType9(...args); +}); +const $$createType10 = $Create.Nullable($$createType9); +const $$createType11 = $Create.Array($$createType4); +const $$createType12 = $Create.Struct({ + "X": $$createType10, + "Y": $$createType11, +}); +const $$createType13 = $Create.Array($$createType12); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js new file mode 100644 index 000000000..9fc31bf7c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js @@ -0,0 +1,23 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Alias} Alias + */ + +/** + * @typedef {$models.Cyclic} Cyclic + */ + +/** + * @template T + * @typedef {$models.GenericCyclic} GenericCyclic + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js new file mode 100644 index 000000000..47d41f572 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js @@ -0,0 +1,20 @@ +// @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 "/wails/runtime.js"; + +/** + * @typedef {Cyclic | null} Alias + */ + +/** + * @typedef {{ [_: string]: Alias }[]} Cyclic + */ + +/** + * @template T + * @typedef {{"X": GenericCyclic | null, "Y": T[]}[]} GenericCyclic + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js new file mode 100644 index 000000000..972196ce3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Classes!"); +console.log("Hello JS!"); +console.log("Hello JS Classes!"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js new file mode 100644 index 000000000..51a9c6be9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.InternalModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByName("main.InternalService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js new file mode 100644 index 000000000..291a3cecf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js @@ -0,0 +1,69 @@ +// @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 "/wails/runtime.js"; + +/** + * An exported but internal model. + */ +export class InternalModel { + /** + * Creates a new InternalModel instance. + * @param {Partial} [$$source = {}] - The source object to create the InternalModel. + */ + constructor($$source = {}) { + if (!("Field" in $$source)) { + /** + * @member + * @type {string} + */ + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new InternalModel instance from a string or object. + * @param {any} [$$source = {}] + * @returns {InternalModel} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new InternalModel(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * An unexported model. + */ +export class unexportedModel { + /** + * Creates a new unexportedModel instance. + * @param {Partial} [$$source = {}] - The source object to create the unexportedModel. + */ + constructor($$source = {}) { + if (!("Field" in $$source)) { + /** + * @member + * @type {string} + */ + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new unexportedModel instance from a string or object. + * @param {any} [$$source = {}] + * @returns {unexportedModel} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new unexportedModel(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js new file mode 100644 index 000000000..b7e83cfd4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Dummy +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js new file mode 100644 index 000000000..274f4eed4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js @@ -0,0 +1,28 @@ +// @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 "/wails/runtime.js"; + +export class Dummy { + /** + * Creates a new Dummy instance. + * @param {Partial} [$$source = {}] - The source object to create the Dummy. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new Dummy instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Dummy} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Dummy(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_j.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_j.js new file mode 100644 index 000000000..2166d33b6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_jc.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_jc.js new file mode 100644 index 000000000..338898726 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_jc.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("JS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js new file mode 100644 index 000000000..c59f8e184 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js @@ -0,0 +1,35 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +/** + * @param {string} $0 + * @returns {$CancellablePromise} + */ +function InternalMethod($0) { + return $Call.ByName("main.Service.InternalMethod", $0); +} + +/** + * @param {otherpackage$0.Dummy} $0 + * @returns {$CancellablePromise} + */ +export function VisibleMethod($0) { + return $Call.ByName("main.Service.VisibleMethod", $0); +} + +/** + * @param {string} arg + * @returns {Promise} + */ +export async function CustomMethod(arg) { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js new file mode 100644 index 000000000..724e79e12 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Classes"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js new file mode 100644 index 000000000..b2f9c5edb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_jc.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_jc.js new file mode 100644 index 000000000..ddf4920e5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_jc.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js new file mode 100644 index 000000000..8a93f316f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.unexportedModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByName("main.unexportedService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js new file mode 100644 index 000000000..494240982 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js @@ -0,0 +1,53 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Comment 1. + * @returns {$CancellablePromise} + */ +export function Method1() { + return $Call.ByName("main.GreetService.Method1"); +} + +/** + * Comment 2. + * @returns {$CancellablePromise} + */ +export function Method2() { + return $Call.ByName("main.GreetService.Method2"); +} + +/** + * Comment 3a. + * Comment 3b. + * @returns {$CancellablePromise} + */ +export function Method3() { + return $Call.ByName("main.GreetService.Method3"); +} + +/** + * Comment 4. + * @returns {$CancellablePromise} + */ +export function Method4() { + return $Call.ByName("main.GreetService.Method4"); +} + +/** + * Comment 5. + * @returns {$CancellablePromise} + */ +export function Method5() { + return $Call.ByName("main.GreetService.Method5"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js new file mode 100644 index 000000000..fd18bf8a3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js @@ -0,0 +1,41 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {$models.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByName("main.GreetService.Greet", name, title); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js new file mode 100644 index 000000000..d5d66d4cb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Person, + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js new file mode 100644 index 000000000..2c5df9ee7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js @@ -0,0 +1,98 @@ +// @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 "/wails/runtime.js"; + +/** + * Age is an integer with some predefined values + * @typedef {number} Age + */ + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Title" in $$source)) { + /** + * @member + * @type {Title} + */ + this["Title"] = Title.$zero; + } + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Age" in $$source)) { + /** + * @member + * @type {Age} + */ + this["Age"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js new file mode 100644 index 000000000..125c76d13 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {services$0.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByName("main.GreetService.Greet", name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js new file mode 100644 index 000000000..089a8b685 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js new file mode 100644 index 000000000..65ebfa2f7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js @@ -0,0 +1,27 @@ +// @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 "/wails/runtime.js"; + +/** + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js new file mode 100644 index 000000000..16a94df37 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js new file mode 100644 index 000000000..0ab295133 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js @@ -0,0 +1,57 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {services$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js new file mode 100644 index 000000000..ba2c614cd --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services.OtherService.Yay").then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js new file mode 100644 index 000000000..16a94df37 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js new file mode 100644 index 000000000..29a95e11e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js @@ -0,0 +1,54 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./services/other/models.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {other$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = other$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js new file mode 100644 index 000000000..f6269f0b5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other.OtherService.Yay").then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js new file mode 100644 index 000000000..e6a0e3a74 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js new file mode 100644 index 000000000..6364fa8f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js new file mode 100644 index 000000000..e91d18592 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js @@ -0,0 +1,30 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function GreetWithContext(name) { + return $Call.ByName("main.GreetService.GreetWithContext", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js new file mode 100644 index 000000000..6364fa8f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js new file mode 100644 index 000000000..9fd9745c1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js @@ -0,0 +1,97 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Maps +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * @template S + * @typedef {$models.BasicCstrAlias} BasicCstrAlias + */ + +/** + * @template R + * @typedef {$models.ComparableCstrAlias} ComparableCstrAlias + */ + +/** + * @typedef {$models.EmbeddedCustomInterface} EmbeddedCustomInterface + */ + +/** + * @typedef {$models.EmbeddedOriginalInterface} EmbeddedOriginalInterface + */ + +/** + * @typedef {$models.EmbeddedPointer} EmbeddedPointer + */ + +/** + * @typedef {$models.EmbeddedPointerPtr} EmbeddedPointerPtr + */ + +/** + * @typedef {$models.EmbeddedValue} EmbeddedValue + */ + +/** + * @typedef {$models.EmbeddedValuePtr} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {$models.GoodTildeCstrAlias} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {$models.InterfaceCstrAlias} InterfaceCstrAlias + */ + +/** + * @template X + * @typedef {$models.MixedCstrAlias} MixedCstrAlias + */ + +/** + * @template V + * @typedef {$models.NonBasicCstrAlias} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {$models.PointableCstrAlias} PointableCstrAlias + */ + +/** + * @typedef {$models.PointerAlias} PointerAlias + */ + +/** + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * @typedef {$models.StringAlias} StringAlias + */ + +/** + * @typedef {$models.StringType} StringType + */ + +/** + * @typedef {$models.ValueAlias} ValueAlias + */ + +/** + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js new file mode 100644 index 000000000..1dc5bfc38 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js @@ -0,0 +1,1957 @@ +// @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 "/wails/runtime.js"; + +/** + * @template S + * @typedef {S} BasicCstrAlias + */ + +/** + * @template R + * @typedef {R} ComparableCstrAlias + */ + +/** + * @typedef {string} EmbeddedCustomInterface + */ + +/** + * @typedef {string} EmbeddedOriginalInterface + */ + +/** + * @typedef {string} EmbeddedPointer + */ + +/** + * @typedef {string} EmbeddedPointerPtr + */ + +/** + * @typedef {string} EmbeddedValue + */ + +/** + * @typedef {string} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {U} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {Y} InterfaceCstrAlias + */ + +/** + * @template R,S,T,U,V,W,X,Y,Z + */ +export class Maps { + /** + * Creates a new Maps instance. + * @param {Partial>} [$$source = {}] - The source object to create the Maps. + */ + constructor($$source = {}) { + if (!("Bool" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["Bool"] = {}; + } + if (!("Int" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Int"] = {}; + } + if (!("Uint" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Uint"] = {}; + } + if (!("Float" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["Float"] = {}; + } + if (!("Complex" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["Complex"] = {}; + } + if (!("Byte" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Byte"] = {}; + } + if (!("Rune" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["Rune"] = {}; + } + if (!("String" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: string]: number }} + */ + this["String"] = {}; + } + if (!("IntPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IntPtr"] = {}; + } + if (!("UintPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["UintPtr"] = {}; + } + if (!("FloatPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["FloatPtr"] = {}; + } + if (!("ComplexPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["ComplexPtr"] = {}; + } + if (!("StringPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["StringPtr"] = {}; + } + if (!("NTM" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["NTM"] = {}; + } + if (!("NTMPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["NTMPtr"] = {}; + } + if (!("VTM" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueTextMarshaler]: number }} + */ + this["VTM"] = {}; + } + if (!("VTMPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueTextMarshaler]: number }} + */ + this["VTMPtr"] = {}; + } + if (!("PTM" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PTM"] = {}; + } + if (!("PTMPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointerTextMarshaler]: number }} + */ + this["PTMPtr"] = {}; + } + if (!("JTM" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["JTM"] = {}; + } + if (!("JTMPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["JTMPtr"] = {}; + } + if (!("A" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["A"] = {}; + } + if (!("APtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["APtr"] = {}; + } + if (!("TM" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TM"] = {}; + } + if (!("TMPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["TMPtr"] = {}; + } + if (!("CI" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["CI"] = {}; + } + if (!("CIPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["CIPtr"] = {}; + } + if (!("EI" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["EI"] = {}; + } + if (!("EIPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["EIPtr"] = {}; + } + if (!("EV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValue]: number }} + */ + this["EV"] = {}; + } + if (!("EVPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValue]: number }} + */ + this["EVPtr"] = {}; + } + if (!("EVP" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValuePtr]: number }} + */ + this["EVP"] = {}; + } + if (!("EVPPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedValuePtr]: number }} + */ + this["EVPPtr"] = {}; + } + if (!("EP" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["EP"] = {}; + } + if (!("EPPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedPointer]: number }} + */ + this["EPPtr"] = {}; + } + if (!("EPP" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedPointerPtr]: number }} + */ + this["EPP"] = {}; + } + if (!("EPPPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedPointerPtr]: number }} + */ + this["EPPPtr"] = {}; + } + if (!("ECI" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedCustomInterface]: number }} + */ + this["ECI"] = {}; + } + if (!("ECIPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedCustomInterface]: number }} + */ + this["ECIPtr"] = {}; + } + if (!("EOI" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedOriginalInterface]: number }} + */ + this["EOI"] = {}; + } + if (!("EOIPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: EmbeddedOriginalInterface]: number }} + */ + this["EOIPtr"] = {}; + } + if (!("WT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["WT"] = {}; + } + if (!("WA" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["WA"] = {}; + } + if (!("ST" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: StringType]: number }} + */ + this["ST"] = {}; + } + if (!("SA" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: StringAlias]: number }} + */ + this["SA"] = {}; + } + if (!("IntT" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["IntT"] = {}; + } + if (!("IntA" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["IntA"] = {}; + } + if (!("VT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VT"] = {}; + } + if (!("VTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VTPtr"] = {}; + } + if (!("VPT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VPT"] = {}; + } + if (!("VPTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VPTPtr"] = {}; + } + if (!("VA" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueAlias]: number }} + */ + this["VA"] = {}; + } + if (!("VAPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ValueAlias]: number }} + */ + this["VAPtr"] = {}; + } + if (!("VPA" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["VPA"] = {}; + } + if (!("VPAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["VPAPtr"] = {}; + } + if (!("PT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PT"] = {}; + } + if (!("PTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PTPtr"] = {}; + } + if (!("PPT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PPT"] = {}; + } + if (!("PPTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PPTPtr"] = {}; + } + if (!("PA" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PA"] = {}; + } + if (!("PAPtr" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointerAlias]: number }} + */ + this["PAPtr"] = {}; + } + if (!("PPA" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["PPA"] = {}; + } + if (!("PPAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["PPAPtr"] = {}; + } + if (!("IT" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["IT"] = {}; + } + if (!("ITPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["ITPtr"] = {}; + } + if (!("IPT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPT"] = {}; + } + if (!("IPTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPTPtr"] = {}; + } + if (!("IA" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["IA"] = {}; + } + if (!("IAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IAPtr"] = {}; + } + if (!("IPA" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPA"] = {}; + } + if (!("IPAPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["IPAPtr"] = {}; + } + if (!("TPR" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPR"] = {}; + } + if (!("TPRPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPRPtr"] = {}; + } + if (!("TPS" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPS"] = {}; + } + if (!("TPSPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPSPtr"] = {}; + } + if (!("TPT" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPT"] = {}; + } + if (!("TPTPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPTPtr"] = {}; + } + if (!("TPU" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPU"] = {}; + } + if (!("TPUPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPUPtr"] = {}; + } + if (!("TPV" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPV"] = {}; + } + if (!("TPVPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPVPtr"] = {}; + } + if (!("TPW" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPW"] = {}; + } + if (!("TPWPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPWPtr"] = {}; + } + if (!("TPX" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPX"] = {}; + } + if (!("TPXPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPXPtr"] = {}; + } + if (!("TPY" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPY"] = {}; + } + if (!("TPYPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPYPtr"] = {}; + } + if (!("TPZ" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["TPZ"] = {}; + } + if (!("TPZPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["TPZPtr"] = {}; + } + if (!("GAR" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAR"] = {}; + } + if (!("GARPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GARPtr"] = {}; + } + if (!("GAS" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAS"] = {}; + } + if (!("GASPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GASPtr"] = {}; + } + if (!("GAT" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAT"] = {}; + } + if (!("GATPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GATPtr"] = {}; + } + if (!("GAU" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAU"] = {}; + } + if (!("GAUPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAUPtr"] = {}; + } + if (!("GAV" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAV"] = {}; + } + if (!("GAVPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAVPtr"] = {}; + } + if (!("GAW" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAW"] = {}; + } + if (!("GAWPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAWPtr"] = {}; + } + if (!("GAX" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAX"] = {}; + } + if (!("GAXPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAXPtr"] = {}; + } + if (!("GAY" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAY"] = {}; + } + if (!("GAYPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAYPtr"] = {}; + } + if (!("GAZ" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAZ"] = {}; + } + if (!("GAZPtr" in $$source)) { + /** + * Soft reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAZPtr"] = {}; + } + if (!("GACi" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["GACi"] = {}; + } + if (!("GACV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: ComparableCstrAlias]: number }} + */ + this["GACV"] = {}; + } + if (!("GACP" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GACP"] = {}; + } + if (!("GACiPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GACiPtr"] = {}; + } + if (!("GACVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GACVPtr"] = {}; + } + if (!("GACPPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GACPPtr"] = {}; + } + if (!("GABi" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["GABi"] = {}; + } + if (!("GABs" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: BasicCstrAlias]: number }} + */ + this["GABs"] = {}; + } + if (!("GABiPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GABiPtr"] = {}; + } + if (!("GABT" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GABT"] = {}; + } + if (!("GABTPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GABTPtr"] = {}; + } + if (!("GAGT" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: GoodTildeCstrAlias]: number }} + */ + this["GAGT"] = {}; + } + if (!("GAGTPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAGTPtr"] = {}; + } + if (!("GANBV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: NonBasicCstrAlias]: number }} + */ + this["GANBV"] = {}; + } + if (!("GANBP" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GANBP"] = {}; + } + if (!("GANBVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GANBVPtr"] = {}; + } + if (!("GANBPPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GANBPPtr"] = {}; + } + if (!("GAPlV1" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointableCstrAlias]: number }} + */ + this["GAPlV1"] = {}; + } + if (!("GAPlV2" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointableCstrAlias]: number }} + */ + this["GAPlV2"] = {}; + } + if (!("GAPlP1" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAPlP1"] = {}; + } + if (!("GAPlP2" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: PointableCstrAlias]: number }} + */ + this["GAPlP2"] = {}; + } + if (!("GAPlVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPlVPtr"] = {}; + } + if (!("GAPlPPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPlPPtr"] = {}; + } + if (!("GAMi" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: `${number}`]: number }} + */ + this["GAMi"] = {}; + } + if (!("GAMS" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: MixedCstrAlias]: number }} + */ + this["GAMS"] = {}; + } + if (!("GAMV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: MixedCstrAlias]: number }} + */ + this["GAMV"] = {}; + } + if (!("GAMSPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAMSPtr"] = {}; + } + if (!("GAMVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAMVPtr"] = {}; + } + if (!("GAII" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAII"] = {}; + } + if (!("GAIV" in $$source)) { + /** + * Accept + * @member + * @type {{ [_: InterfaceCstrAlias]: number }} + */ + this["GAIV"] = {}; + } + if (!("GAIP" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAIP"] = {}; + } + if (!("GAIIPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAIIPtr"] = {}; + } + if (!("GAIVPtr" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAIVPtr"] = {}; + } + if (!("GAIPPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAIPPtr"] = {}; + } + if (!("GAPrV" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrV"] = {}; + } + if (!("GAPrP" in $$source)) { + /** + * Accept, hide + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrP"] = {}; + } + if (!("GAPrVPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrVPtr"] = {}; + } + if (!("GAPrPPtr" in $$source)) { + /** + * Reject + * @member + * @type {{ [_: string]: number }} + */ + this["GAPrPPtr"] = {}; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class Maps. + * @template [R=any] + * @template [S=any] + * @template [T=any] + * @template [U=any] + * @template [V=any] + * @template [W=any] + * @template [X=any] + * @template [Y=any] + * @template [Z=any] + * @param {(source: any) => R} $$createParamR + * @param {(source: any) => S} $$createParamS + * @param {(source: any) => T} $$createParamT + * @param {(source: any) => U} $$createParamU + * @param {(source: any) => V} $$createParamV + * @param {(source: any) => W} $$createParamW + * @param {(source: any) => X} $$createParamX + * @param {(source: any) => Y} $$createParamY + * @param {(source: any) => Z} $$createParamZ + * @returns {($$source?: any) => Maps} + */ + static createFrom($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType3; + const $$createField4_0 = $$createType4; + const $$createField5_0 = $$createType5; + const $$createField6_0 = $$createType6; + const $$createField7_0 = $$createType7; + const $$createField8_0 = $$createType8; + const $$createField9_0 = $$createType9; + const $$createField10_0 = $$createType10; + const $$createField11_0 = $$createType11; + const $$createField12_0 = $$createType12; + const $$createField13_0 = $$createType13; + const $$createField14_0 = $$createType14; + const $$createField15_0 = $$createType15; + const $$createField16_0 = $$createType16; + const $$createField17_0 = $$createType17; + const $$createField18_0 = $$createType18; + const $$createField19_0 = $$createType19; + const $$createField20_0 = $$createType20; + const $$createField21_0 = $$createType21; + const $$createField22_0 = $$createType22; + const $$createField23_0 = $$createType23; + const $$createField24_0 = $$createType24; + const $$createField25_0 = $$createType25; + const $$createField26_0 = $$createType26; + const $$createField27_0 = $$createType27; + const $$createField28_0 = $$createType28; + const $$createField29_0 = $$createType29; + const $$createField30_0 = $$createType30; + const $$createField31_0 = $$createType31; + const $$createField32_0 = $$createType32; + const $$createField33_0 = $$createType33; + const $$createField34_0 = $$createType34; + const $$createField35_0 = $$createType35; + const $$createField36_0 = $$createType36; + const $$createField37_0 = $$createType37; + const $$createField38_0 = $$createType38; + const $$createField39_0 = $$createType39; + const $$createField40_0 = $$createType40; + const $$createField41_0 = $$createType41; + const $$createField42_0 = $$createType0; + const $$createField43_0 = $$createType42; + const $$createField44_0 = $$createType7; + const $$createField45_0 = $$createType43; + const $$createField46_0 = $$createType1; + const $$createField47_0 = $$createType44; + const $$createField48_0 = $$createType45; + const $$createField49_0 = $$createType46; + const $$createField50_0 = $$createType47; + const $$createField51_0 = $$createType15; + const $$createField52_0 = $$createType16; + const $$createField53_0 = $$createType16; + const $$createField54_0 = $$createType48; + const $$createField55_0 = $$createType49; + const $$createField56_0 = $$createType50; + const $$createField57_0 = $$createType51; + const $$createField58_0 = $$createType52; + const $$createField59_0 = $$createType17; + const $$createField60_0 = $$createType18; + const $$createField61_0 = $$createType18; + const $$createField62_0 = $$createType53; + const $$createField63_0 = $$createType54; + const $$createField64_0 = $$createType55; + const $$createField65_0 = $$createType56; + const $$createField66_0 = $$createType57; + const $$createField67_0 = $$createType23; + const $$createField68_0 = $$createType24; + const $$createField69_0 = $$createType24; + const $$createField70_0 = $$createType58; + const $$createField71_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField72_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField73_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField74_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField75_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField76_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField77_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField78_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField79_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField80_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField81_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField82_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField83_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField84_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField85_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField86_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField87_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField88_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField89_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField90_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField91_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField92_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField93_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField94_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField95_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField96_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField97_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField98_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField99_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField100_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField101_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField102_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField103_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField104_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField105_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField106_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField107_0 = $$createType1; + const $$createField108_0 = $$createType15; + const $$createField109_0 = $$createType17; + const $$createField110_0 = $$createType8; + const $$createField111_0 = $$createType16; + const $$createField112_0 = $$createType18; + const $$createField113_0 = $$createType1; + const $$createField114_0 = $$createType7; + const $$createField115_0 = $$createType8; + const $$createField116_0 = $$createType77; + const $$createField117_0 = $$createType78; + const $$createField118_0 = $$createType15; + const $$createField119_0 = $$createType16; + const $$createField120_0 = $$createType15; + const $$createField121_0 = $$createType18; + const $$createField122_0 = $$createType16; + const $$createField123_0 = $$createType53; + const $$createField124_0 = $$createType15; + const $$createField125_0 = $$createType16; + const $$createField126_0 = $$createType17; + const $$createField127_0 = $$createType18; + const $$createField128_0 = $$createType16; + const $$createField129_0 = $$createType18; + const $$createField130_0 = $$createType2; + const $$createField131_0 = $$createType42; + const $$createField132_0 = $$createType15; + const $$createField133_0 = $$createType79; + const $$createField134_0 = $$createType16; + const $$createField135_0 = $$createType23; + const $$createField136_0 = $$createType15; + const $$createField137_0 = $$createType18; + const $$createField138_0 = $$createType24; + const $$createField139_0 = $$createType16; + const $$createField140_0 = $$createType53; + const $$createField141_0 = $$createType16; + const $$createField142_0 = $$createType18; + const $$createField143_0 = $$createType48; + const $$createField144_0 = $$createType53; + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Bool" in $$parsedSource) { + $$parsedSource["Bool"] = $$createField0_0($$parsedSource["Bool"]); + } + if ("Int" in $$parsedSource) { + $$parsedSource["Int"] = $$createField1_0($$parsedSource["Int"]); + } + if ("Uint" in $$parsedSource) { + $$parsedSource["Uint"] = $$createField2_0($$parsedSource["Uint"]); + } + if ("Float" in $$parsedSource) { + $$parsedSource["Float"] = $$createField3_0($$parsedSource["Float"]); + } + if ("Complex" in $$parsedSource) { + $$parsedSource["Complex"] = $$createField4_0($$parsedSource["Complex"]); + } + if ("Byte" in $$parsedSource) { + $$parsedSource["Byte"] = $$createField5_0($$parsedSource["Byte"]); + } + if ("Rune" in $$parsedSource) { + $$parsedSource["Rune"] = $$createField6_0($$parsedSource["Rune"]); + } + if ("String" in $$parsedSource) { + $$parsedSource["String"] = $$createField7_0($$parsedSource["String"]); + } + if ("IntPtr" in $$parsedSource) { + $$parsedSource["IntPtr"] = $$createField8_0($$parsedSource["IntPtr"]); + } + if ("UintPtr" in $$parsedSource) { + $$parsedSource["UintPtr"] = $$createField9_0($$parsedSource["UintPtr"]); + } + if ("FloatPtr" in $$parsedSource) { + $$parsedSource["FloatPtr"] = $$createField10_0($$parsedSource["FloatPtr"]); + } + if ("ComplexPtr" in $$parsedSource) { + $$parsedSource["ComplexPtr"] = $$createField11_0($$parsedSource["ComplexPtr"]); + } + if ("StringPtr" in $$parsedSource) { + $$parsedSource["StringPtr"] = $$createField12_0($$parsedSource["StringPtr"]); + } + if ("NTM" in $$parsedSource) { + $$parsedSource["NTM"] = $$createField13_0($$parsedSource["NTM"]); + } + if ("NTMPtr" in $$parsedSource) { + $$parsedSource["NTMPtr"] = $$createField14_0($$parsedSource["NTMPtr"]); + } + if ("VTM" in $$parsedSource) { + $$parsedSource["VTM"] = $$createField15_0($$parsedSource["VTM"]); + } + if ("VTMPtr" in $$parsedSource) { + $$parsedSource["VTMPtr"] = $$createField16_0($$parsedSource["VTMPtr"]); + } + if ("PTM" in $$parsedSource) { + $$parsedSource["PTM"] = $$createField17_0($$parsedSource["PTM"]); + } + if ("PTMPtr" in $$parsedSource) { + $$parsedSource["PTMPtr"] = $$createField18_0($$parsedSource["PTMPtr"]); + } + if ("JTM" in $$parsedSource) { + $$parsedSource["JTM"] = $$createField19_0($$parsedSource["JTM"]); + } + if ("JTMPtr" in $$parsedSource) { + $$parsedSource["JTMPtr"] = $$createField20_0($$parsedSource["JTMPtr"]); + } + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField21_0($$parsedSource["A"]); + } + if ("APtr" in $$parsedSource) { + $$parsedSource["APtr"] = $$createField22_0($$parsedSource["APtr"]); + } + if ("TM" in $$parsedSource) { + $$parsedSource["TM"] = $$createField23_0($$parsedSource["TM"]); + } + if ("TMPtr" in $$parsedSource) { + $$parsedSource["TMPtr"] = $$createField24_0($$parsedSource["TMPtr"]); + } + if ("CI" in $$parsedSource) { + $$parsedSource["CI"] = $$createField25_0($$parsedSource["CI"]); + } + if ("CIPtr" in $$parsedSource) { + $$parsedSource["CIPtr"] = $$createField26_0($$parsedSource["CIPtr"]); + } + if ("EI" in $$parsedSource) { + $$parsedSource["EI"] = $$createField27_0($$parsedSource["EI"]); + } + if ("EIPtr" in $$parsedSource) { + $$parsedSource["EIPtr"] = $$createField28_0($$parsedSource["EIPtr"]); + } + if ("EV" in $$parsedSource) { + $$parsedSource["EV"] = $$createField29_0($$parsedSource["EV"]); + } + if ("EVPtr" in $$parsedSource) { + $$parsedSource["EVPtr"] = $$createField30_0($$parsedSource["EVPtr"]); + } + if ("EVP" in $$parsedSource) { + $$parsedSource["EVP"] = $$createField31_0($$parsedSource["EVP"]); + } + if ("EVPPtr" in $$parsedSource) { + $$parsedSource["EVPPtr"] = $$createField32_0($$parsedSource["EVPPtr"]); + } + if ("EP" in $$parsedSource) { + $$parsedSource["EP"] = $$createField33_0($$parsedSource["EP"]); + } + if ("EPPtr" in $$parsedSource) { + $$parsedSource["EPPtr"] = $$createField34_0($$parsedSource["EPPtr"]); + } + if ("EPP" in $$parsedSource) { + $$parsedSource["EPP"] = $$createField35_0($$parsedSource["EPP"]); + } + if ("EPPPtr" in $$parsedSource) { + $$parsedSource["EPPPtr"] = $$createField36_0($$parsedSource["EPPPtr"]); + } + if ("ECI" in $$parsedSource) { + $$parsedSource["ECI"] = $$createField37_0($$parsedSource["ECI"]); + } + if ("ECIPtr" in $$parsedSource) { + $$parsedSource["ECIPtr"] = $$createField38_0($$parsedSource["ECIPtr"]); + } + if ("EOI" in $$parsedSource) { + $$parsedSource["EOI"] = $$createField39_0($$parsedSource["EOI"]); + } + if ("EOIPtr" in $$parsedSource) { + $$parsedSource["EOIPtr"] = $$createField40_0($$parsedSource["EOIPtr"]); + } + if ("WT" in $$parsedSource) { + $$parsedSource["WT"] = $$createField41_0($$parsedSource["WT"]); + } + if ("WA" in $$parsedSource) { + $$parsedSource["WA"] = $$createField42_0($$parsedSource["WA"]); + } + if ("ST" in $$parsedSource) { + $$parsedSource["ST"] = $$createField43_0($$parsedSource["ST"]); + } + if ("SA" in $$parsedSource) { + $$parsedSource["SA"] = $$createField44_0($$parsedSource["SA"]); + } + if ("IntT" in $$parsedSource) { + $$parsedSource["IntT"] = $$createField45_0($$parsedSource["IntT"]); + } + if ("IntA" in $$parsedSource) { + $$parsedSource["IntA"] = $$createField46_0($$parsedSource["IntA"]); + } + if ("VT" in $$parsedSource) { + $$parsedSource["VT"] = $$createField47_0($$parsedSource["VT"]); + } + if ("VTPtr" in $$parsedSource) { + $$parsedSource["VTPtr"] = $$createField48_0($$parsedSource["VTPtr"]); + } + if ("VPT" in $$parsedSource) { + $$parsedSource["VPT"] = $$createField49_0($$parsedSource["VPT"]); + } + if ("VPTPtr" in $$parsedSource) { + $$parsedSource["VPTPtr"] = $$createField50_0($$parsedSource["VPTPtr"]); + } + if ("VA" in $$parsedSource) { + $$parsedSource["VA"] = $$createField51_0($$parsedSource["VA"]); + } + if ("VAPtr" in $$parsedSource) { + $$parsedSource["VAPtr"] = $$createField52_0($$parsedSource["VAPtr"]); + } + if ("VPA" in $$parsedSource) { + $$parsedSource["VPA"] = $$createField53_0($$parsedSource["VPA"]); + } + if ("VPAPtr" in $$parsedSource) { + $$parsedSource["VPAPtr"] = $$createField54_0($$parsedSource["VPAPtr"]); + } + if ("PT" in $$parsedSource) { + $$parsedSource["PT"] = $$createField55_0($$parsedSource["PT"]); + } + if ("PTPtr" in $$parsedSource) { + $$parsedSource["PTPtr"] = $$createField56_0($$parsedSource["PTPtr"]); + } + if ("PPT" in $$parsedSource) { + $$parsedSource["PPT"] = $$createField57_0($$parsedSource["PPT"]); + } + if ("PPTPtr" in $$parsedSource) { + $$parsedSource["PPTPtr"] = $$createField58_0($$parsedSource["PPTPtr"]); + } + if ("PA" in $$parsedSource) { + $$parsedSource["PA"] = $$createField59_0($$parsedSource["PA"]); + } + if ("PAPtr" in $$parsedSource) { + $$parsedSource["PAPtr"] = $$createField60_0($$parsedSource["PAPtr"]); + } + if ("PPA" in $$parsedSource) { + $$parsedSource["PPA"] = $$createField61_0($$parsedSource["PPA"]); + } + if ("PPAPtr" in $$parsedSource) { + $$parsedSource["PPAPtr"] = $$createField62_0($$parsedSource["PPAPtr"]); + } + if ("IT" in $$parsedSource) { + $$parsedSource["IT"] = $$createField63_0($$parsedSource["IT"]); + } + if ("ITPtr" in $$parsedSource) { + $$parsedSource["ITPtr"] = $$createField64_0($$parsedSource["ITPtr"]); + } + if ("IPT" in $$parsedSource) { + $$parsedSource["IPT"] = $$createField65_0($$parsedSource["IPT"]); + } + if ("IPTPtr" in $$parsedSource) { + $$parsedSource["IPTPtr"] = $$createField66_0($$parsedSource["IPTPtr"]); + } + if ("IA" in $$parsedSource) { + $$parsedSource["IA"] = $$createField67_0($$parsedSource["IA"]); + } + if ("IAPtr" in $$parsedSource) { + $$parsedSource["IAPtr"] = $$createField68_0($$parsedSource["IAPtr"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField69_0($$parsedSource["IPA"]); + } + if ("IPAPtr" in $$parsedSource) { + $$parsedSource["IPAPtr"] = $$createField70_0($$parsedSource["IPAPtr"]); + } + if ("TPR" in $$parsedSource) { + $$parsedSource["TPR"] = $$createField71_0($$parsedSource["TPR"]); + } + if ("TPRPtr" in $$parsedSource) { + $$parsedSource["TPRPtr"] = $$createField72_0($$parsedSource["TPRPtr"]); + } + if ("TPS" in $$parsedSource) { + $$parsedSource["TPS"] = $$createField73_0($$parsedSource["TPS"]); + } + if ("TPSPtr" in $$parsedSource) { + $$parsedSource["TPSPtr"] = $$createField74_0($$parsedSource["TPSPtr"]); + } + if ("TPT" in $$parsedSource) { + $$parsedSource["TPT"] = $$createField75_0($$parsedSource["TPT"]); + } + if ("TPTPtr" in $$parsedSource) { + $$parsedSource["TPTPtr"] = $$createField76_0($$parsedSource["TPTPtr"]); + } + if ("TPU" in $$parsedSource) { + $$parsedSource["TPU"] = $$createField77_0($$parsedSource["TPU"]); + } + if ("TPUPtr" in $$parsedSource) { + $$parsedSource["TPUPtr"] = $$createField78_0($$parsedSource["TPUPtr"]); + } + if ("TPV" in $$parsedSource) { + $$parsedSource["TPV"] = $$createField79_0($$parsedSource["TPV"]); + } + if ("TPVPtr" in $$parsedSource) { + $$parsedSource["TPVPtr"] = $$createField80_0($$parsedSource["TPVPtr"]); + } + if ("TPW" in $$parsedSource) { + $$parsedSource["TPW"] = $$createField81_0($$parsedSource["TPW"]); + } + if ("TPWPtr" in $$parsedSource) { + $$parsedSource["TPWPtr"] = $$createField82_0($$parsedSource["TPWPtr"]); + } + if ("TPX" in $$parsedSource) { + $$parsedSource["TPX"] = $$createField83_0($$parsedSource["TPX"]); + } + if ("TPXPtr" in $$parsedSource) { + $$parsedSource["TPXPtr"] = $$createField84_0($$parsedSource["TPXPtr"]); + } + if ("TPY" in $$parsedSource) { + $$parsedSource["TPY"] = $$createField85_0($$parsedSource["TPY"]); + } + if ("TPYPtr" in $$parsedSource) { + $$parsedSource["TPYPtr"] = $$createField86_0($$parsedSource["TPYPtr"]); + } + if ("TPZ" in $$parsedSource) { + $$parsedSource["TPZ"] = $$createField87_0($$parsedSource["TPZ"]); + } + if ("TPZPtr" in $$parsedSource) { + $$parsedSource["TPZPtr"] = $$createField88_0($$parsedSource["TPZPtr"]); + } + if ("GAR" in $$parsedSource) { + $$parsedSource["GAR"] = $$createField89_0($$parsedSource["GAR"]); + } + if ("GARPtr" in $$parsedSource) { + $$parsedSource["GARPtr"] = $$createField90_0($$parsedSource["GARPtr"]); + } + if ("GAS" in $$parsedSource) { + $$parsedSource["GAS"] = $$createField91_0($$parsedSource["GAS"]); + } + if ("GASPtr" in $$parsedSource) { + $$parsedSource["GASPtr"] = $$createField92_0($$parsedSource["GASPtr"]); + } + if ("GAT" in $$parsedSource) { + $$parsedSource["GAT"] = $$createField93_0($$parsedSource["GAT"]); + } + if ("GATPtr" in $$parsedSource) { + $$parsedSource["GATPtr"] = $$createField94_0($$parsedSource["GATPtr"]); + } + if ("GAU" in $$parsedSource) { + $$parsedSource["GAU"] = $$createField95_0($$parsedSource["GAU"]); + } + if ("GAUPtr" in $$parsedSource) { + $$parsedSource["GAUPtr"] = $$createField96_0($$parsedSource["GAUPtr"]); + } + if ("GAV" in $$parsedSource) { + $$parsedSource["GAV"] = $$createField97_0($$parsedSource["GAV"]); + } + if ("GAVPtr" in $$parsedSource) { + $$parsedSource["GAVPtr"] = $$createField98_0($$parsedSource["GAVPtr"]); + } + if ("GAW" in $$parsedSource) { + $$parsedSource["GAW"] = $$createField99_0($$parsedSource["GAW"]); + } + if ("GAWPtr" in $$parsedSource) { + $$parsedSource["GAWPtr"] = $$createField100_0($$parsedSource["GAWPtr"]); + } + if ("GAX" in $$parsedSource) { + $$parsedSource["GAX"] = $$createField101_0($$parsedSource["GAX"]); + } + if ("GAXPtr" in $$parsedSource) { + $$parsedSource["GAXPtr"] = $$createField102_0($$parsedSource["GAXPtr"]); + } + if ("GAY" in $$parsedSource) { + $$parsedSource["GAY"] = $$createField103_0($$parsedSource["GAY"]); + } + if ("GAYPtr" in $$parsedSource) { + $$parsedSource["GAYPtr"] = $$createField104_0($$parsedSource["GAYPtr"]); + } + if ("GAZ" in $$parsedSource) { + $$parsedSource["GAZ"] = $$createField105_0($$parsedSource["GAZ"]); + } + if ("GAZPtr" in $$parsedSource) { + $$parsedSource["GAZPtr"] = $$createField106_0($$parsedSource["GAZPtr"]); + } + if ("GACi" in $$parsedSource) { + $$parsedSource["GACi"] = $$createField107_0($$parsedSource["GACi"]); + } + if ("GACV" in $$parsedSource) { + $$parsedSource["GACV"] = $$createField108_0($$parsedSource["GACV"]); + } + if ("GACP" in $$parsedSource) { + $$parsedSource["GACP"] = $$createField109_0($$parsedSource["GACP"]); + } + if ("GACiPtr" in $$parsedSource) { + $$parsedSource["GACiPtr"] = $$createField110_0($$parsedSource["GACiPtr"]); + } + if ("GACVPtr" in $$parsedSource) { + $$parsedSource["GACVPtr"] = $$createField111_0($$parsedSource["GACVPtr"]); + } + if ("GACPPtr" in $$parsedSource) { + $$parsedSource["GACPPtr"] = $$createField112_0($$parsedSource["GACPPtr"]); + } + if ("GABi" in $$parsedSource) { + $$parsedSource["GABi"] = $$createField113_0($$parsedSource["GABi"]); + } + if ("GABs" in $$parsedSource) { + $$parsedSource["GABs"] = $$createField114_0($$parsedSource["GABs"]); + } + if ("GABiPtr" in $$parsedSource) { + $$parsedSource["GABiPtr"] = $$createField115_0($$parsedSource["GABiPtr"]); + } + if ("GABT" in $$parsedSource) { + $$parsedSource["GABT"] = $$createField116_0($$parsedSource["GABT"]); + } + if ("GABTPtr" in $$parsedSource) { + $$parsedSource["GABTPtr"] = $$createField117_0($$parsedSource["GABTPtr"]); + } + if ("GAGT" in $$parsedSource) { + $$parsedSource["GAGT"] = $$createField118_0($$parsedSource["GAGT"]); + } + if ("GAGTPtr" in $$parsedSource) { + $$parsedSource["GAGTPtr"] = $$createField119_0($$parsedSource["GAGTPtr"]); + } + if ("GANBV" in $$parsedSource) { + $$parsedSource["GANBV"] = $$createField120_0($$parsedSource["GANBV"]); + } + if ("GANBP" in $$parsedSource) { + $$parsedSource["GANBP"] = $$createField121_0($$parsedSource["GANBP"]); + } + if ("GANBVPtr" in $$parsedSource) { + $$parsedSource["GANBVPtr"] = $$createField122_0($$parsedSource["GANBVPtr"]); + } + if ("GANBPPtr" in $$parsedSource) { + $$parsedSource["GANBPPtr"] = $$createField123_0($$parsedSource["GANBPPtr"]); + } + if ("GAPlV1" in $$parsedSource) { + $$parsedSource["GAPlV1"] = $$createField124_0($$parsedSource["GAPlV1"]); + } + if ("GAPlV2" in $$parsedSource) { + $$parsedSource["GAPlV2"] = $$createField125_0($$parsedSource["GAPlV2"]); + } + if ("GAPlP1" in $$parsedSource) { + $$parsedSource["GAPlP1"] = $$createField126_0($$parsedSource["GAPlP1"]); + } + if ("GAPlP2" in $$parsedSource) { + $$parsedSource["GAPlP2"] = $$createField127_0($$parsedSource["GAPlP2"]); + } + if ("GAPlVPtr" in $$parsedSource) { + $$parsedSource["GAPlVPtr"] = $$createField128_0($$parsedSource["GAPlVPtr"]); + } + if ("GAPlPPtr" in $$parsedSource) { + $$parsedSource["GAPlPPtr"] = $$createField129_0($$parsedSource["GAPlPPtr"]); + } + if ("GAMi" in $$parsedSource) { + $$parsedSource["GAMi"] = $$createField130_0($$parsedSource["GAMi"]); + } + if ("GAMS" in $$parsedSource) { + $$parsedSource["GAMS"] = $$createField131_0($$parsedSource["GAMS"]); + } + if ("GAMV" in $$parsedSource) { + $$parsedSource["GAMV"] = $$createField132_0($$parsedSource["GAMV"]); + } + if ("GAMSPtr" in $$parsedSource) { + $$parsedSource["GAMSPtr"] = $$createField133_0($$parsedSource["GAMSPtr"]); + } + if ("GAMVPtr" in $$parsedSource) { + $$parsedSource["GAMVPtr"] = $$createField134_0($$parsedSource["GAMVPtr"]); + } + if ("GAII" in $$parsedSource) { + $$parsedSource["GAII"] = $$createField135_0($$parsedSource["GAII"]); + } + if ("GAIV" in $$parsedSource) { + $$parsedSource["GAIV"] = $$createField136_0($$parsedSource["GAIV"]); + } + if ("GAIP" in $$parsedSource) { + $$parsedSource["GAIP"] = $$createField137_0($$parsedSource["GAIP"]); + } + if ("GAIIPtr" in $$parsedSource) { + $$parsedSource["GAIIPtr"] = $$createField138_0($$parsedSource["GAIIPtr"]); + } + if ("GAIVPtr" in $$parsedSource) { + $$parsedSource["GAIVPtr"] = $$createField139_0($$parsedSource["GAIVPtr"]); + } + if ("GAIPPtr" in $$parsedSource) { + $$parsedSource["GAIPPtr"] = $$createField140_0($$parsedSource["GAIPPtr"]); + } + if ("GAPrV" in $$parsedSource) { + $$parsedSource["GAPrV"] = $$createField141_0($$parsedSource["GAPrV"]); + } + if ("GAPrP" in $$parsedSource) { + $$parsedSource["GAPrP"] = $$createField142_0($$parsedSource["GAPrP"]); + } + if ("GAPrVPtr" in $$parsedSource) { + $$parsedSource["GAPrVPtr"] = $$createField143_0($$parsedSource["GAPrVPtr"]); + } + if ("GAPrPPtr" in $$parsedSource) { + $$parsedSource["GAPrPPtr"] = $$createField144_0($$parsedSource["GAPrPPtr"]); + } + return new Maps(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +/** + * @template X + * @typedef {X} MixedCstrAlias + */ + +/** + * @template V + * @typedef {V} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {W} PointableCstrAlias + */ + +/** + * @typedef {PointerTextMarshaler} PointerAlias + */ + +/** + * @typedef {string} PointerTextMarshaler + */ + +/** + * @typedef {string} StringAlias + */ + +/** + * @typedef {string} StringType + */ + +/** + * @typedef {ValueTextMarshaler} ValueAlias + */ + +/** + * @typedef {string} ValueTextMarshaler + */ + +// Private type creation functions +const $$createType0 = $Create.Map($Create.Any, $Create.Any); +const $$createType1 = $Create.Map($Create.Any, $Create.Any); +const $$createType2 = $Create.Map($Create.Any, $Create.Any); +const $$createType3 = $Create.Map($Create.Any, $Create.Any); +const $$createType4 = $Create.Map($Create.Any, $Create.Any); +const $$createType5 = $Create.Map($Create.Any, $Create.Any); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Map($Create.Any, $Create.Any); +const $$createType8 = $Create.Map($Create.Any, $Create.Any); +const $$createType9 = $Create.Map($Create.Any, $Create.Any); +const $$createType10 = $Create.Map($Create.Any, $Create.Any); +const $$createType11 = $Create.Map($Create.Any, $Create.Any); +const $$createType12 = $Create.Map($Create.Any, $Create.Any); +const $$createType13 = $Create.Map($Create.Any, $Create.Any); +const $$createType14 = $Create.Map($Create.Any, $Create.Any); +const $$createType15 = $Create.Map($Create.Any, $Create.Any); +const $$createType16 = $Create.Map($Create.Any, $Create.Any); +const $$createType17 = $Create.Map($Create.Any, $Create.Any); +const $$createType18 = $Create.Map($Create.Any, $Create.Any); +const $$createType19 = $Create.Map($Create.Any, $Create.Any); +const $$createType20 = $Create.Map($Create.Any, $Create.Any); +const $$createType21 = $Create.Map($Create.Any, $Create.Any); +const $$createType22 = $Create.Map($Create.Any, $Create.Any); +const $$createType23 = $Create.Map($Create.Any, $Create.Any); +const $$createType24 = $Create.Map($Create.Any, $Create.Any); +const $$createType25 = $Create.Map($Create.Any, $Create.Any); +const $$createType26 = $Create.Map($Create.Any, $Create.Any); +const $$createType27 = $Create.Map($Create.Any, $Create.Any); +const $$createType28 = $Create.Map($Create.Any, $Create.Any); +const $$createType29 = $Create.Map($Create.Any, $Create.Any); +const $$createType30 = $Create.Map($Create.Any, $Create.Any); +const $$createType31 = $Create.Map($Create.Any, $Create.Any); +const $$createType32 = $Create.Map($Create.Any, $Create.Any); +const $$createType33 = $Create.Map($Create.Any, $Create.Any); +const $$createType34 = $Create.Map($Create.Any, $Create.Any); +const $$createType35 = $Create.Map($Create.Any, $Create.Any); +const $$createType36 = $Create.Map($Create.Any, $Create.Any); +const $$createType37 = $Create.Map($Create.Any, $Create.Any); +const $$createType38 = $Create.Map($Create.Any, $Create.Any); +const $$createType39 = $Create.Map($Create.Any, $Create.Any); +const $$createType40 = $Create.Map($Create.Any, $Create.Any); +const $$createType41 = $Create.Map($Create.Any, $Create.Any); +const $$createType42 = $Create.Map($Create.Any, $Create.Any); +const $$createType43 = $Create.Map($Create.Any, $Create.Any); +const $$createType44 = $Create.Map($Create.Any, $Create.Any); +const $$createType45 = $Create.Map($Create.Any, $Create.Any); +const $$createType46 = $Create.Map($Create.Any, $Create.Any); +const $$createType47 = $Create.Map($Create.Any, $Create.Any); +const $$createType48 = $Create.Map($Create.Any, $Create.Any); +const $$createType49 = $Create.Map($Create.Any, $Create.Any); +const $$createType50 = $Create.Map($Create.Any, $Create.Any); +const $$createType51 = $Create.Map($Create.Any, $Create.Any); +const $$createType52 = $Create.Map($Create.Any, $Create.Any); +const $$createType53 = $Create.Map($Create.Any, $Create.Any); +const $$createType54 = $Create.Map($Create.Any, $Create.Any); +const $$createType55 = $Create.Map($Create.Any, $Create.Any); +const $$createType56 = $Create.Map($Create.Any, $Create.Any); +const $$createType57 = $Create.Map($Create.Any, $Create.Any); +const $$createType58 = $Create.Map($Create.Any, $Create.Any); +const $$createType59 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType60 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType61 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType62 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType63 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType64 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType65 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType66 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType67 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType68 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType69 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType70 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType71 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType72 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType73 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType74 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType75 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType76 = /** @type {(...args: any[]) => any} */(($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ) => $Create.Map($Create.Any, $Create.Any)); +const $$createType77 = $Create.Map($Create.Any, $Create.Any); +const $$createType78 = $Create.Map($Create.Any, $Create.Any); +const $$createType79 = $Create.Map($Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js new file mode 100644 index 000000000..cc98f3a89 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js @@ -0,0 +1,23 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>>} + */ +export function Method() { + return $Call.ByName("main.Service.Method").then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Maps.createFrom($Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js new file mode 100644 index 000000000..8f525252e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js @@ -0,0 +1,116 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Data, + ImplicitNonMarshaler, + NonMarshaler +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * any + * @typedef {$models.AliasJsonMarshaler} AliasJsonMarshaler + */ + +/** + * any + * @typedef {$models.AliasMarshaler} AliasMarshaler + */ + +/** + * struct{} + * @typedef {$models.AliasNonMarshaler} AliasNonMarshaler + */ + +/** + * string + * @typedef {$models.AliasTextMarshaler} AliasTextMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitJsonButText} ImplicitJsonButText + */ + +/** + * any + * @typedef {$models.ImplicitJsonMarshaler} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitMarshaler} ImplicitMarshaler + */ + +/** + * string + * @typedef {$models.ImplicitNonJson} ImplicitNonJson + */ + +/** + * any + * @typedef {$models.ImplicitNonText} ImplicitNonText + */ + +/** + * any + * @typedef {$models.ImplicitTextButJson} ImplicitTextButJson + */ + +/** + * string + * @typedef {$models.ImplicitTextMarshaler} ImplicitTextMarshaler + */ + +/** + * any + * @typedef {$models.PointerJsonMarshaler} PointerJsonMarshaler + */ + +/** + * any + * @typedef {$models.PointerMarshaler} PointerMarshaler + */ + +/** + * string + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingJsonMarshaler} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingMarshaler} UnderlyingMarshaler + */ + +/** + * string + * @typedef {$models.UnderlyingTextMarshaler} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {$models.ValueJsonMarshaler} ValueJsonMarshaler + */ + +/** + * any + * @typedef {$models.ValueMarshaler} ValueMarshaler + */ + +/** + * string + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js new file mode 100644 index 000000000..77fe552ea --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js @@ -0,0 +1,630 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + * @typedef {any} AliasJsonMarshaler + */ + +/** + * any + * @typedef {any} AliasMarshaler + */ + +/** + * struct{} + * @typedef { { + * } } AliasNonMarshaler + */ + +/** + * string + * @typedef {string} AliasTextMarshaler + */ + +export class Data { + /** + * Creates a new Data instance. + * @param {Partial} [$$source = {}] - The source object to create the Data. + */ + constructor($$source = {}) { + if (!("NM" in $$source)) { + /** + * @member + * @type {NonMarshaler} + */ + this["NM"] = (new NonMarshaler()); + } + if (!("NMPtr" in $$source)) { + /** + * NonMarshaler | null + * @member + * @type {NonMarshaler | null} + */ + this["NMPtr"] = null; + } + if (!("VJM" in $$source)) { + /** + * @member + * @type {ValueJsonMarshaler} + */ + this["VJM"] = null; + } + if (!("VJMPtr" in $$source)) { + /** + * ValueJsonMarshaler | null + * @member + * @type {ValueJsonMarshaler | null} + */ + this["VJMPtr"] = null; + } + if (!("PJM" in $$source)) { + /** + * @member + * @type {PointerJsonMarshaler} + */ + this["PJM"] = null; + } + if (!("PJMPtr" in $$source)) { + /** + * PointerJsonMarshaler | null + * @member + * @type {PointerJsonMarshaler | null} + */ + this["PJMPtr"] = null; + } + if (!("VTM" in $$source)) { + /** + * @member + * @type {ValueTextMarshaler} + */ + this["VTM"] = ""; + } + if (!("VTMPtr" in $$source)) { + /** + * ValueTextMarshaler | null + * @member + * @type {ValueTextMarshaler | null} + */ + this["VTMPtr"] = null; + } + if (!("PTM" in $$source)) { + /** + * @member + * @type {PointerTextMarshaler} + */ + this["PTM"] = ""; + } + if (!("PTMPtr" in $$source)) { + /** + * PointerTextMarshaler | null + * @member + * @type {PointerTextMarshaler | null} + */ + this["PTMPtr"] = null; + } + if (!("VM" in $$source)) { + /** + * @member + * @type {ValueMarshaler} + */ + this["VM"] = null; + } + if (!("VMPtr" in $$source)) { + /** + * ValueMarshaler | null + * @member + * @type {ValueMarshaler | null} + */ + this["VMPtr"] = null; + } + if (!("PM" in $$source)) { + /** + * @member + * @type {PointerMarshaler} + */ + this["PM"] = null; + } + if (!("PMPtr" in $$source)) { + /** + * PointerMarshaler | null + * @member + * @type {PointerMarshaler | null} + */ + this["PMPtr"] = null; + } + if (!("UJM" in $$source)) { + /** + * @member + * @type {UnderlyingJsonMarshaler} + */ + this["UJM"] = null; + } + if (!("UJMPtr" in $$source)) { + /** + * UnderlyingJsonMarshaler | null + * @member + * @type {UnderlyingJsonMarshaler | null} + */ + this["UJMPtr"] = null; + } + if (!("UTM" in $$source)) { + /** + * @member + * @type {UnderlyingTextMarshaler} + */ + this["UTM"] = ""; + } + if (!("UTMPtr" in $$source)) { + /** + * UnderlyingTextMarshaler | null + * @member + * @type {UnderlyingTextMarshaler | null} + */ + this["UTMPtr"] = null; + } + if (!("UM" in $$source)) { + /** + * @member + * @type {UnderlyingMarshaler} + */ + this["UM"] = null; + } + if (!("UMPtr" in $$source)) { + /** + * UnderlyingMarshaler | null + * @member + * @type {UnderlyingMarshaler | null} + */ + this["UMPtr"] = null; + } + if (!("JM" in $$source)) { + /** + * any + * @member + * @type {any} + */ + this["JM"] = null; + } + if (!("JMPtr" in $$source)) { + /** + * any | null + * @member + * @type {any | null} + */ + this["JMPtr"] = null; + } + if (!("TM" in $$source)) { + /** + * string + * @member + * @type {string} + */ + this["TM"] = ""; + } + if (!("TMPtr" in $$source)) { + /** + * string | null + * @member + * @type {string | null} + */ + this["TMPtr"] = null; + } + if (!("CJM" in $$source)) { + /** + * any + * @member + * @type {any} + */ + this["CJM"] = null; + } + if (!("CJMPtr" in $$source)) { + /** + * any | null + * @member + * @type {any | null} + */ + this["CJMPtr"] = null; + } + if (!("CTM" in $$source)) { + /** + * string + * @member + * @type {string} + */ + this["CTM"] = ""; + } + if (!("CTMPtr" in $$source)) { + /** + * string | null + * @member + * @type {string | null} + */ + this["CTMPtr"] = null; + } + if (!("CM" in $$source)) { + /** + * any + * @member + * @type {any} + */ + this["CM"] = null; + } + if (!("CMPtr" in $$source)) { + /** + * any | null + * @member + * @type {any | null} + */ + this["CMPtr"] = null; + } + if (!("ANM" in $$source)) { + /** + * @member + * @type {AliasNonMarshaler} + */ + this["ANM"] = {}; + } + if (!("ANMPtr" in $$source)) { + /** + * AliasNonMarshaler | null + * @member + * @type {AliasNonMarshaler | null} + */ + this["ANMPtr"] = null; + } + if (!("AJM" in $$source)) { + /** + * @member + * @type {AliasJsonMarshaler} + */ + this["AJM"] = null; + } + if (!("AJMPtr" in $$source)) { + /** + * AliasJsonMarshaler | null + * @member + * @type {AliasJsonMarshaler | null} + */ + this["AJMPtr"] = null; + } + if (!("ATM" in $$source)) { + /** + * @member + * @type {AliasTextMarshaler} + */ + this["ATM"] = ""; + } + if (!("ATMPtr" in $$source)) { + /** + * AliasTextMarshaler | null + * @member + * @type {AliasTextMarshaler | null} + */ + this["ATMPtr"] = null; + } + if (!("AM" in $$source)) { + /** + * @member + * @type {AliasMarshaler} + */ + this["AM"] = null; + } + if (!("AMPtr" in $$source)) { + /** + * AliasMarshaler | null + * @member + * @type {AliasMarshaler | null} + */ + this["AMPtr"] = null; + } + if (!("ImJM" in $$source)) { + /** + * @member + * @type {ImplicitJsonMarshaler} + */ + this["ImJM"] = null; + } + if (!("ImJMPtr" in $$source)) { + /** + * ImplicitJsonMarshaler | null + * @member + * @type {ImplicitJsonMarshaler | null} + */ + this["ImJMPtr"] = null; + } + if (!("ImTM" in $$source)) { + /** + * @member + * @type {ImplicitTextMarshaler} + */ + this["ImTM"] = ""; + } + if (!("ImTMPtr" in $$source)) { + /** + * ImplicitTextMarshaler | null + * @member + * @type {ImplicitTextMarshaler | null} + */ + this["ImTMPtr"] = null; + } + if (!("ImM" in $$source)) { + /** + * @member + * @type {ImplicitMarshaler} + */ + this["ImM"] = null; + } + if (!("ImMPtr" in $$source)) { + /** + * ImplicitMarshaler | null + * @member + * @type {ImplicitMarshaler | null} + */ + this["ImMPtr"] = null; + } + if (!("ImNJ" in $$source)) { + /** + * @member + * @type {ImplicitNonJson} + */ + this["ImNJ"] = ""; + } + if (!("ImNJPtr" in $$source)) { + /** + * ImplicitNonJson | null + * @member + * @type {ImplicitNonJson | null} + */ + this["ImNJPtr"] = null; + } + if (!("ImNT" in $$source)) { + /** + * @member + * @type {ImplicitNonText} + */ + this["ImNT"] = null; + } + if (!("ImNTPtr" in $$source)) { + /** + * ImplicitNonText | null + * @member + * @type {ImplicitNonText | null} + */ + this["ImNTPtr"] = null; + } + if (!("ImNM" in $$source)) { + /** + * @member + * @type {ImplicitNonMarshaler} + */ + this["ImNM"] = (new ImplicitNonMarshaler()); + } + if (!("ImNMPtr" in $$source)) { + /** + * ImplicitNonMarshaler | null + * @member + * @type {ImplicitNonMarshaler | null} + */ + this["ImNMPtr"] = null; + } + if (!("ImJbT" in $$source)) { + /** + * @member + * @type {ImplicitJsonButText} + */ + this["ImJbT"] = null; + } + if (!("ImJbTPtr" in $$source)) { + /** + * ImplicitJsonButText | null + * @member + * @type {ImplicitJsonButText | null} + */ + this["ImJbTPtr"] = null; + } + if (!("ImTbJ" in $$source)) { + /** + * @member + * @type {ImplicitTextButJson} + */ + this["ImTbJ"] = null; + } + if (!("ImTbJPtr" in $$source)) { + /** + * ImplicitTextButJson | null + * @member + * @type {ImplicitTextButJson | null} + */ + this["ImTbJPtr"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Data instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Data} + */ + static createFrom($$source = {}) { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField48_0 = $$createType2; + const $$createField49_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("NM" in $$parsedSource) { + $$parsedSource["NM"] = $$createField0_0($$parsedSource["NM"]); + } + if ("NMPtr" in $$parsedSource) { + $$parsedSource["NMPtr"] = $$createField1_0($$parsedSource["NMPtr"]); + } + if ("ImNM" in $$parsedSource) { + $$parsedSource["ImNM"] = $$createField48_0($$parsedSource["ImNM"]); + } + if ("ImNMPtr" in $$parsedSource) { + $$parsedSource["ImNMPtr"] = $$createField49_0($$parsedSource["ImNMPtr"]); + } + return new Data(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * any + * @typedef {any} ImplicitJsonButText + */ + +/** + * any + * @typedef {any} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {any} ImplicitMarshaler + */ + +/** + * string + * @typedef {string} ImplicitNonJson + */ + +/** + * class{ Marshaler, TextMarshaler } + */ +export class ImplicitNonMarshaler { + /** + * Creates a new ImplicitNonMarshaler instance. + * @param {Partial} [$$source = {}] - The source object to create the ImplicitNonMarshaler. + */ + constructor($$source = {}) { + if (!("Marshaler" in $$source)) { + /** + * @member + * @type {json$0.Marshaler} + */ + this["Marshaler"] = null; + } + if (!("TextMarshaler" in $$source)) { + /** + * @member + * @type {encoding$0.TextMarshaler} + */ + this["TextMarshaler"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new ImplicitNonMarshaler instance from a string or object. + * @param {any} [$$source = {}] + * @returns {ImplicitNonMarshaler} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new ImplicitNonMarshaler(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * any + * @typedef {any} ImplicitNonText + */ + +/** + * any + * @typedef {any} ImplicitTextButJson + */ + +/** + * string + * @typedef {string} ImplicitTextMarshaler + */ + +/** + * class {} + */ +export class NonMarshaler { + /** + * Creates a new NonMarshaler instance. + * @param {Partial} [$$source = {}] - The source object to create the NonMarshaler. + */ + constructor($$source = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new NonMarshaler instance from a string or object. + * @param {any} [$$source = {}] + * @returns {NonMarshaler} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new NonMarshaler(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * any + * @typedef {any} PointerJsonMarshaler + */ + +/** + * any + * @typedef {any} PointerMarshaler + */ + +/** + * string + * @typedef {string} PointerTextMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingMarshaler + */ + +/** + * string + * @typedef {string} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {any} ValueJsonMarshaler + */ + +/** + * any + * @typedef {any} ValueMarshaler + */ + +/** + * string + * @typedef {string} ValueTextMarshaler + */ + +// Private type creation functions +const $$createType0 = NonMarshaler.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = ImplicitNonMarshaler.createFrom; +const $$createType3 = $Create.Nullable($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js new file mode 100644 index 000000000..e6c23e4f0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js @@ -0,0 +1,23 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Data>} + */ +export function Method() { + return $Call.ByName("main.Service.Method").then(/** @type {($result: any) => any} */(($result) => { + return $$createType0($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Data.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js new file mode 100644 index 000000000..9be766c03 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +export { + HowDifferent, + Impersonator, + Person, + PrivatePerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js new file mode 100644 index 000000000..b42e223fa --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js @@ -0,0 +1,181 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + * @template How + */ +export class HowDifferent { + /** + * Creates a new HowDifferent instance. + * @param {Partial>} [$$source = {}] - The source object to create the HowDifferent. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * They have a name as well. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + /** + * But they may have many differences. + * @member + * @type {{ [_: string]: How }[]} + */ + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class HowDifferent. + * @template [How=any] + * @param {(source: any) => How} $$createParamHow + * @returns {($$source?: any) => HowDifferent} + */ + static createFrom($$createParamHow) { + const $$createField1_0 = $$createType1($$createParamHow); + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new HowDifferent(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +/** + * Impersonator gets their fields from other people. + */ +export const Impersonator = other$0.OtherPerson; + +/** + * Impersonator gets their fields from other people. + * @typedef {other$0.OtherPerson} Impersonator + */ + +/** + * Person is not a number. + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * They have a name. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + /** + * Exactly 4 sketchy friends. + * @member + * @type {Impersonator[]} + */ + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField1_0($$parsedSource["Friends"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +export class personImpl { + /** + * Creates a new personImpl instance. + * @param {Partial} [$$source = {}] - The source object to create the personImpl. + */ + constructor($$source = {}) { + if (!("Nickname" in $$source)) { + /** + * Nickname conceals a person's identity. + * @member + * @type {string} + */ + this["Nickname"] = ""; + } + if (!("Name" in $$source)) { + /** + * They have a name. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + /** + * Exactly 4 sketchy friends. + * @member + * @type {Impersonator[]} + */ + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new personImpl instance from a string or object. + * @param {any} [$$source = {}] + * @returns {personImpl} + */ + static createFrom($$source = {}) { + const $$createField2_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField2_0($$parsedSource["Friends"]); + } + return new personImpl(/** @type {Partial} */($$parsedSource)); + } +} + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export const PrivatePerson = personImpl; + +/** + * PrivatePerson gets their fields from hidden sources. + * @typedef {personImpl} PrivatePerson + */ + +// Private type creation functions +const $$createType0 = /** @type {(...args: any[]) => any} */(($$createParamHow) => $Create.Map($Create.Any, $$createParamHow)); +const $$createType1 = /** @type {(...args: any[]) => any} */(($$createParamHow) => $Create.Array($$createType0($$createParamHow))); +const $$createType2 = other$0.OtherPerson.createFrom($Create.Any); +const $$createType3 = $Create.Array($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js new file mode 100644 index 000000000..db4e64147 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +export { + OtherPerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js new file mode 100644 index 000000000..89992cacf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js @@ -0,0 +1,60 @@ +// @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 "/wails/runtime.js"; + +/** + * OtherPerson is like a person, but different. + * @template T + */ +export class OtherPerson { + /** + * Creates a new OtherPerson instance. + * @param {Partial>} [$$source = {}] - The source object to create the OtherPerson. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * They have a name as well. + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + /** + * But they may have many differences. + * @member + * @type {T[]} + */ + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class OtherPerson. + * @template [T=any] + * @param {(source: any) => T} $$createParamT + * @returns {($$source?: any) => OtherPerson} + */ + static createFrom($$createParamT) { + const $$createField1_0 = $$createType0($$createParamT); + return ($$source = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new OtherPerson(/** @type {Partial>} */($$parsedSource)); + }; + } +} + +// Private type creation functions +const $$createType0 = /** @type {(...args: any[]) => any} */(($$createParamT) => $Create.Array($$createParamT)); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js new file mode 100644 index 000000000..d2819bc4b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other.OtherMethods.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js new file mode 100644 index 000000000..35bc3579d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js @@ -0,0 +1,42 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOne").then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + })); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOtherOne"); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.HowDifferent.createFrom($Create.Any); +const $$createType2 = $models.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js new file mode 100644 index 000000000..0497b16bc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("main.EmbedOther.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js new file mode 100644 index 000000000..903e4153f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js @@ -0,0 +1,42 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByName("main.EmbedService.LikeThisOne").then(/** @type {($result: any) => any} */(($result) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + })); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("main.EmbedService.LikeThisOtherOne"); +} + +// Private type creation functions +const $$createType0 = nobindingshere$0.Person.createFrom; +const $$createType1 = nobindingshere$0.HowDifferent.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js new file mode 100644 index 000000000..fe683f548 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} $0 + * @returns {$CancellablePromise} + */ +export function Greet($0) { + return $Call.ByName("main.GreetService.Greet", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js new file mode 100644 index 000000000..734fb02e7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js new file mode 100644 index 000000000..e6a0e3a74 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js new file mode 100644 index 000000000..69652f626 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js new file mode 100644 index 000000000..e6a0e3a74 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js new file mode 100644 index 000000000..69652f626 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js new file mode 100644 index 000000000..16a94df37 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js new file mode 100644 index 000000000..04771d2ca --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js @@ -0,0 +1,54 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {services$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js new file mode 100644 index 000000000..327b15c9c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services.OtherService.Yay").then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js new file mode 100644 index 000000000..d811d97ce --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js @@ -0,0 +1,379 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +/** + * @param {{ [_: `${number}`]: number }} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number | null }} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] }>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType3($result); + })); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType4($result); + })); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js new file mode 100644 index 000000000..69c96370a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js @@ -0,0 +1,57 @@ +// @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 "/wails/runtime.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + /** + * @member + * @type {Person | null} + */ + this["Parent"] = null; + } + if (!("Details" in $$source)) { + /** + * @member + * @type {{"Age": number, "Address": {"Street": string}}} + */ + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js new file mode 100644 index 000000000..d811d97ce --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js @@ -0,0 +1,379 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +/** + * @param {{ [_: `${number}`]: number }} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number | null }} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] }} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] }>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType2($result); + })); +} + +/** + * @param {string[]} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType3($result); + })); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in).then(/** @type {($result: any) => any} */(($result) => { + return $$createType4($result); + })); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js new file mode 100644 index 000000000..69c96370a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js @@ -0,0 +1,57 @@ +// @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 "/wails/runtime.js"; + +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + /** + * @member + * @type {Person | null} + */ + this["Parent"] = null; + } + if (!("Details" in $$source)) { + /** + * @member + * @type {{"Age": number, "Address": {"Street": string}}} + */ + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js new file mode 100644 index 000000000..6364fa8f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js new file mode 100644 index 000000000..6364fa8f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js new file mode 100644 index 000000000..16a94df37 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js @@ -0,0 +1,40 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name).then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js new file mode 100644 index 000000000..fefe6b6ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js new file mode 100644 index 000000000..d7bfe75cf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js @@ -0,0 +1,58 @@ +// @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + */ +export class Person { + /** + * Creates a new Person instance. + * @param {Partial} [$$source = {}] - The source object to create the Person. + */ + constructor($$source = {}) { + if (!("Name" in $$source)) { + /** + * @member + * @type {string} + */ + this["Name"] = ""; + } + if (!("Address" in $$source)) { + /** + * @member + * @type {services$0.Address | null} + */ + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Person} + */ + static createFrom($$source = {}) { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person(/** @type {Partial} */($$parsedSource)); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js new file mode 100644 index 000000000..ed65b6d15 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js new file mode 100644 index 000000000..24a5de807 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js @@ -0,0 +1,49 @@ +// @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 "/wails/runtime.js"; + +export class Address { + /** + * Creates a new Address instance. + * @param {Partial
} [$$source = {}] - The source object to create the Address. + */ + constructor($$source = {}) { + if (!("Street" in $$source)) { + /** + * @member + * @type {string} + */ + this["Street"] = ""; + } + if (!("State" in $$source)) { + /** + * @member + * @type {string} + */ + this["State"] = ""; + } + if (!("Country" in $$source)) { + /** + * @member + * @type {string} + */ + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Address} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address(/** @type {Partial
} */($$parsedSource)); + } +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js new file mode 100644 index 000000000..d413366d6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js @@ -0,0 +1,31 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services.OtherService.Yay").then(/** @type {($result: any) => any} */(($result) => { + return $$createType1($result); + })); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/warnings.log b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=false/UseNames=true/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/index.js new file mode 100644 index 000000000..cf48d86db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/index.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {$models.TextMarshaler} TextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/json/index.js new file mode 100644 index 000000000..22f1fd904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/json/index.js @@ -0,0 +1,11 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {$models.Marshaler} Marshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/json/models.js new file mode 100644 index 000000000..5dce4eea6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/json/models.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {any} Marshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/models.js new file mode 100644 index 000000000..db89bafbc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/encoding/models.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {any} TextMarshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js new file mode 100644 index 000000000..16b76f94c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js @@ -0,0 +1,70 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + * @param {$models.Alias} aliasValue + * @returns {$CancellablePromise<$models.Person>} + */ +export function Get(aliasValue) { + return $Call.ByID(1928502664, aliasValue); +} + +/** + * Apparently, aliases are all the rage right now. + * @param {$models.AliasedPerson} p + * @returns {$CancellablePromise<$models.StrangelyAliasedPerson>} + */ +export function GetButAliased(p) { + return $Call.ByID(1896499664, p); +} + +/** + * Get someone quite different. + * @returns {$CancellablePromise<$models.GenericPerson>} + */ +export function GetButDifferent() { + return $Call.ByID(2240931744); +} + +/** + * @returns {$CancellablePromise} + */ +export function GetButForeignPrivateAlias() { + return $Call.ByID(643456960); +} + +/** + * @returns {$CancellablePromise<$models.AliasGroup>} + */ +export function GetButGenericAliases() { + return $Call.ByID(914093800); +} + +/** + * Greet a lot of unusual things. + * @param {$models.EmptyAliasStruct} $0 + * @param {$models.EmptyStruct} $1 + * @returns {$CancellablePromise<$models.AliasStruct>} + */ +export function Greet($0, $1) { + return $Call.ByID(1411160069, $0, $1); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js new file mode 100644 index 000000000..5a5c62644 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js @@ -0,0 +1,96 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * A nice type Alias. + * @typedef {$models.Alias} Alias + */ + +/** + * A class whose fields have various aliased types. + * @typedef {$models.AliasGroup} AliasGroup + */ + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {$models.AliasStruct} AliasStruct + */ + +/** + * A class alias. + * @typedef {$models.AliasedPerson} AliasedPerson + */ + +/** + * An empty struct alias. + * @typedef {$models.EmptyAliasStruct} EmptyAliasStruct + */ + +/** + * An empty struct. + * @typedef {$models.EmptyStruct} EmptyStruct + */ + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {$models.GenericAlias} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {$models.GenericMapAlias} GenericMapAlias + */ + +/** + * A generic struct containing an alias. + * @template T + * @typedef {$models.GenericPerson} GenericPerson + */ + +/** + * A generic alias that wraps a generic struct. + * @template T + * @typedef {$models.GenericPersonAlias} GenericPersonAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {$models.GenericPtrAlias} GenericPtrAlias + */ + +/** + * An alias that wraps a class through a non-typeparam alias. + * @typedef {$models.IndirectPersonAlias} IndirectPersonAlias + */ + +/** + * Another struct alias. + * @typedef {$models.OtherAliasStruct} OtherAliasStruct + */ + +/** + * A non-generic struct containing an alias. + * @typedef {$models.Person} Person + */ + +/** + * Another class alias, but ordered after its aliased class. + * @typedef {$models.StrangelyAliasedPerson} StrangelyAliasedPerson + */ + +/** + * An alias that wraps a class through a typeparam alias. + * @typedef {$models.TPIndirectPersonAlias} TPIndirectPersonAlias + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js new file mode 100644 index 000000000..1ba2af395 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js @@ -0,0 +1,112 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * A nice type Alias. + * @typedef {number} Alias + */ + +/** + * A class whose fields have various aliased types. + * @typedef {Object} AliasGroup + * @property {GenericAlias} GAi + * @property {GenericAlias>} GAP + * @property {GenericPtrAlias} GPAs + * @property {GenericPtrAlias>} GPAP + * @property {GenericMapAlias} GMA + * @property {GenericPersonAlias} GPA + * @property {IndirectPersonAlias} IPA + * @property {TPIndirectPersonAlias} TPIPA + */ + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {Object} AliasStruct + * @property {number[] | null} Foo - A field with a comment. + * @property {string} [Bar] - Definitely not Foo. + * @property {string} [Baz] - Definitely not Foo. + * @property {OtherAliasStruct} Other - A nested alias struct. + */ + +/** + * A class alias. + * @typedef {Person} AliasedPerson + */ + +/** + * An empty struct alias. + * @typedef { { + * } } EmptyAliasStruct + */ + +/** + * An empty struct. + * @typedef { { + * } } EmptyStruct + */ + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {T} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {{ [_: string]: U } | null} GenericMapAlias + */ + +/** + * A generic struct containing an alias. + * @template T + * @typedef {Object} GenericPerson + * @property {T} Name + * @property {Alias} AliasedField + */ + +/** + * A generic alias that wraps a generic struct. + * @template T + * @typedef {GenericPerson[] | null>} GenericPersonAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {GenericAlias | null} GenericPtrAlias + */ + +/** + * An alias that wraps a class through a non-typeparam alias. + * @typedef {GenericPersonAlias} IndirectPersonAlias + */ + +/** + * Another struct alias. + * @typedef {Object} OtherAliasStruct + * @property {number[] | null} NoMoreIdeas + */ + +/** + * A non-generic struct containing an alias. + * @typedef {Object} Person + * @property {string} Name - The Person's name. + * @property {Alias} AliasedField - A random alias field. + */ + +/** + * Another class alias, but ordered after its aliased class. + * @typedef {Person} StrangelyAliasedPerson + */ + +/** + * An alias that wraps a class through a typeparam alias. + * @typedef {GenericAlias>} TPIndirectPersonAlias + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js new file mode 100644 index 000000000..f6c839720 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js new file mode 100644 index 000000000..2a4188bce --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod() { + return $Call.ByID(2241101727); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js new file mode 100644 index 000000000..ead6f5da4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod2() { + return $Call.ByID(1556848345); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js new file mode 100644 index 000000000..995d46c13 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {$models.Person} person + * @param {$models.Embedded1} emb + * @returns {$CancellablePromise} + */ +export function Greet(person, emb) { + return $Call.ByID(1411160069, person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js new file mode 100644 index 000000000..f21130b86 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js @@ -0,0 +1,27 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Title +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Embedded1} Embedded1 + */ + +/** + * @typedef {$models.Embedded3} Embedded3 + */ + +/** + * Person represents a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js new file mode 100644 index 000000000..b30630777 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js @@ -0,0 +1,64 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Embedded1 + * @property {number} Friends - Friends should be shadowed in Person by a field of lesser depth + * @property {number} Vanish - Vanish should be omitted from Person because there is another field with same depth and no tag + * @property {string} StillThere - StillThere should be shadowed in Person by other field with same depth and a json tag + * @property {`${boolean}`} NamingThingsIsHard - NamingThingsIsHard is a law of programming + */ + +/** + * @typedef {string} Embedded3 + */ + +/** + * Person represents a person + * @typedef { { + * "Titles"?: Title[] | null, + * "Names": string[] | null, + * "Partner": Person | null, + * "Friends": (Person | null)[] | null, + * "NamingThingsIsHard": `${boolean}`, + * "StillThere": Embedded3 | null, + * "-": number, + * "Embedded3": Embedded3, + * "StrangerNumber": `${number}`, + * "StrangestString"?: `"${string}"`, + * "StringStrangest"?: `"${string}"`, + * "emb4"?: embedded4, + * } } Person + */ + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +/** + * @typedef {Object} embedded4 + * @property {`${boolean}`} NamingThingsIsHard - NamingThingsIsHard is a law of programming + * @property {boolean} Friends - Friends should not be shadowed in Person as embedded4 is not embedded from encoding/json's point of view; however, it should be shadowed in Embedded1 + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js new file mode 100644 index 000000000..0285f6ca4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + * @param {string} str + * @param {$models.Person[] | null} people + * @param {{"AnotherCount": number, "AnotherOne": $models.Person | null}} $2 + * @param {{ [_: `${number}`]: boolean | null } | null} assoc + * @param {(number | null)[] | null} $4 + * @param {string[]} other + * @returns {$CancellablePromise<[$models.Person, any, number[] | null]>} + */ +export function Greet(str, people, $2, assoc, $4, ...other) { + return $Call.ByID(1411160069, str, people, $2, assoc, $4, other); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js new file mode 100644 index 000000000..88af1203b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * Person represents a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js new file mode 100644 index 000000000..035bc0792 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Person represents a person + * @typedef {Object} Person + * @property {string} Name + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js new file mode 100644 index 000000000..53027124b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.StructA, $models.StructC]>} + */ +export function MakeCycles() { + return $Call.ByID(440020721); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js new file mode 100644 index 000000000..9b49a9172 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js @@ -0,0 +1,22 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.StructA} StructA + */ + +/** + * @typedef {$models.StructC} StructC + */ + +/** + * @typedef {$models.StructE} StructE + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js new file mode 100644 index 000000000..50e0d0fc0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} StructA + * @property {structB | null} B + */ + +/** + * @typedef {Object} StructC + * @property {structD} D + */ + +/** + * @typedef { { + * } } StructE + */ + +/** + * @typedef {Object} structB + * @property {StructA | null} A + */ + +/** + * @typedef {Object} structD + * @property {StructE} E + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js new file mode 100644 index 000000000..382e6bac3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]>} + */ +export function MakeCycles() { + return $Call.ByID(440020721); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js new file mode 100644 index 000000000..9fc31bf7c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js @@ -0,0 +1,23 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Alias} Alias + */ + +/** + * @typedef {$models.Cyclic} Cyclic + */ + +/** + * @template T + * @typedef {$models.GenericCyclic} GenericCyclic + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js new file mode 100644 index 000000000..2413995ac --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Cyclic | null} Alias + */ + +/** + * @typedef {({ [_: string]: Alias } | null)[] | null} Cyclic + */ + +/** + * @template T + * @typedef {{"X": GenericCyclic | null, "Y": T[] | null}[] | null} GenericCyclic + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js new file mode 100644 index 000000000..40d68bf85 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Interfaces!"); +console.log("Hello JS!"); +console.log("Hello JS Interfaces!"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js new file mode 100644 index 000000000..f96a46e77 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.InternalModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByID(538079117, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js new file mode 100644 index 000000000..a4c233c8c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js @@ -0,0 +1,19 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal model. + * @typedef {Object} InternalModel + * @property {string} Field + */ + +/** + * An unexported model. + * @typedef {Object} unexportedModel + * @property {string} Field + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js new file mode 100644 index 000000000..c93da8f05 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js @@ -0,0 +1,9 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Dummy} Dummy + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js new file mode 100644 index 000000000..6b6f5401f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef { { + * } } Dummy + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js new file mode 100644 index 000000000..0d869be84 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js @@ -0,0 +1,35 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +/** + * @param {string} $0 + * @returns {$CancellablePromise} + */ +function InternalMethod($0) { + return $Call.ByID(3518775569, $0); +} + +/** + * @param {otherpackage$0.Dummy} $0 + * @returns {$CancellablePromise} + */ +export function VisibleMethod($0) { + return $Call.ByID(474018228, $0); +} + +/** + * @param {string} arg + * @returns {Promise} + */ +export async function CustomMethod(arg) { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js new file mode 100644 index 000000000..442f20472 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js new file mode 100644 index 000000000..b2f9c5edb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ji.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ji.js new file mode 100644 index 000000000..36e28f09b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ji.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js new file mode 100644 index 000000000..573852d5a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.unexportedModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByID(37626172, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js new file mode 100644 index 000000000..d364009d6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js @@ -0,0 +1,53 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Comment 1. + * @returns {$CancellablePromise} + */ +export function Method1() { + return $Call.ByID(841558284); +} + +/** + * Comment 2. + * @returns {$CancellablePromise} + */ +export function Method2() { + return $Call.ByID(891891141); +} + +/** + * Comment 3a. + * Comment 3b. + * @returns {$CancellablePromise} + */ +export function Method3() { + return $Call.ByID(875113522); +} + +/** + * Comment 4. + * @returns {$CancellablePromise} + */ +export function Method4() { + return $Call.ByID(791225427); +} + +/** + * Comment 5. + * @returns {$CancellablePromise} + */ +export function Method5() { + return $Call.ByID(774447808); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js new file mode 100644 index 000000000..f14b3b6b2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js @@ -0,0 +1,35 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {$models.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByID(1411160069, name, title); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js new file mode 100644 index 000000000..649d8d016 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Title +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * Person represents a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js new file mode 100644 index 000000000..e7c70729c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js @@ -0,0 +1,61 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Age is an integer with some predefined values + * @typedef {number} Age + */ + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + * @typedef {Object} Person + * @property {Title} Title + * @property {string} Name + * @property {Age} Age + */ + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js new file mode 100644 index 000000000..508c12b72 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {services$0.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByID(1411160069, name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js new file mode 100644 index 000000000..089a8b685 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js new file mode 100644 index 000000000..e0e2d3014 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js @@ -0,0 +1,27 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js new file mode 100644 index 000000000..40d2245b9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js new file mode 100644 index 000000000..26922b7eb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * Person is a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js new file mode 100644 index 000000000..5743a9055 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js @@ -0,0 +1,18 @@ +// @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 * as services$0 from "./services/models.js"; + +/** + * Person is a person + * @typedef {Object} Person + * @property {string} Name + * @property {services$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js new file mode 100644 index 000000000..dff4e0d5f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(2007737399); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js new file mode 100644 index 000000000..40d2245b9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js new file mode 100644 index 000000000..8a9890617 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js @@ -0,0 +1,17 @@ +// @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 * as other$0 from "./services/other/models.js"; + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {other$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js new file mode 100644 index 000000000..182a9e091 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(2447353446); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js new file mode 100644 index 000000000..9343d5531 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js new file mode 100644 index 000000000..6c1448d81 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js new file mode 100644 index 000000000..be0cf5ce8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js @@ -0,0 +1,30 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function GreetWithContext(name) { + return $Call.ByID(1310150960, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js new file mode 100644 index 000000000..6c1448d81 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js new file mode 100644 index 000000000..80fdcd24c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js @@ -0,0 +1,98 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +import * as $models from "./models.js"; + +/** + * @template S + * @typedef {$models.BasicCstrAlias} BasicCstrAlias + */ + +/** + * @template R + * @typedef {$models.ComparableCstrAlias} ComparableCstrAlias + */ + +/** + * @typedef {$models.EmbeddedCustomInterface} EmbeddedCustomInterface + */ + +/** + * @typedef {$models.EmbeddedOriginalInterface} EmbeddedOriginalInterface + */ + +/** + * @typedef {$models.EmbeddedPointer} EmbeddedPointer + */ + +/** + * @typedef {$models.EmbeddedPointerPtr} EmbeddedPointerPtr + */ + +/** + * @typedef {$models.EmbeddedValue} EmbeddedValue + */ + +/** + * @typedef {$models.EmbeddedValuePtr} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {$models.GoodTildeCstrAlias} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {$models.InterfaceCstrAlias} InterfaceCstrAlias + */ + +/** + * @template R,S,T,U,V,W,X,Y,Z + * @typedef {$models.Maps} Maps + */ + +/** + * @template X + * @typedef {$models.MixedCstrAlias} MixedCstrAlias + */ + +/** + * @template V + * @typedef {$models.NonBasicCstrAlias} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {$models.PointableCstrAlias} PointableCstrAlias + */ + +/** + * @typedef {$models.PointerAlias} PointerAlias + */ + +/** + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * @typedef {$models.StringAlias} StringAlias + */ + +/** + * @typedef {$models.StringType} StringType + */ + +/** + * @typedef {$models.ValueAlias} ValueAlias + */ + +/** + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js new file mode 100644 index 000000000..cee61a5e4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js @@ -0,0 +1,240 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @template S + * @typedef {S} BasicCstrAlias + */ + +/** + * @template R + * @typedef {R} ComparableCstrAlias + */ + +/** + * @typedef {string} EmbeddedCustomInterface + */ + +/** + * @typedef {string} EmbeddedOriginalInterface + */ + +/** + * @typedef {string} EmbeddedPointer + */ + +/** + * @typedef {string} EmbeddedPointerPtr + */ + +/** + * @typedef {string} EmbeddedValue + */ + +/** + * @typedef {string} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {U} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {Y} InterfaceCstrAlias + */ + +/** + * @template R,S,T,U,V,W,X,Y,Z + * @typedef {Object} Maps + * @property {{ [_: string]: number } | null} Bool - Reject + * @property {{ [_: `${number}`]: number } | null} Int - Accept + * @property {{ [_: `${number}`]: number } | null} Uint - Accept + * @property {{ [_: string]: number } | null} Float - Reject + * @property {{ [_: string]: number } | null} Complex - Reject + * @property {{ [_: `${number}`]: number } | null} Byte - Accept + * @property {{ [_: `${number}`]: number } | null} Rune - Accept + * @property {{ [_: string]: number } | null} String - Accept + * @property {{ [_: string]: number } | null} IntPtr - Reject + * @property {{ [_: string]: number } | null} UintPtr - Reject + * @property {{ [_: string]: number } | null} FloatPtr - Reject + * @property {{ [_: string]: number } | null} ComplexPtr - Reject + * @property {{ [_: string]: number } | null} StringPtr - Reject + * @property {{ [_: string]: number } | null} NTM - Reject + * @property {{ [_: string]: number } | null} NTMPtr - Reject + * @property {{ [_: ValueTextMarshaler]: number } | null} VTM - Accept + * @property {{ [_: ValueTextMarshaler]: number } | null} VTMPtr - Accept + * @property {{ [_: string]: number } | null} PTM - Reject + * @property {{ [_: PointerTextMarshaler]: number } | null} PTMPtr - Accept + * @property {{ [_: string]: number } | null} JTM - Accept, hide + * @property {{ [_: string]: number } | null} JTMPtr - Accept, hide + * @property {{ [_: string]: number } | null} A - Reject + * @property {{ [_: string]: number } | null} APtr - Reject + * @property {{ [_: string]: number } | null} TM - Accept, hide + * @property {{ [_: string]: number } | null} TMPtr - Reject + * @property {{ [_: string]: number } | null} CI - Accept, hide + * @property {{ [_: string]: number } | null} CIPtr - Reject + * @property {{ [_: string]: number } | null} EI - Accept, hide + * @property {{ [_: string]: number } | null} EIPtr - Reject + * @property {{ [_: EmbeddedValue]: number } | null} EV - Accept + * @property {{ [_: EmbeddedValue]: number } | null} EVPtr - Accept + * @property {{ [_: EmbeddedValuePtr]: number } | null} EVP - Accept + * @property {{ [_: EmbeddedValuePtr]: number } | null} EVPPtr - Accept + * @property {{ [_: string]: number } | null} EP - Reject + * @property {{ [_: EmbeddedPointer]: number } | null} EPPtr - Accept + * @property {{ [_: EmbeddedPointerPtr]: number } | null} EPP - Accept + * @property {{ [_: EmbeddedPointerPtr]: number } | null} EPPPtr - Accept + * @property {{ [_: EmbeddedCustomInterface]: number } | null} ECI - Accept + * @property {{ [_: EmbeddedCustomInterface]: number } | null} ECIPtr - Accept + * @property {{ [_: EmbeddedOriginalInterface]: number } | null} EOI - Accept + * @property {{ [_: EmbeddedOriginalInterface]: number } | null} EOIPtr - Accept + * @property {{ [_: string]: number } | null} WT - Reject + * @property {{ [_: string]: number } | null} WA - Reject + * @property {{ [_: StringType]: number } | null} ST - Accept + * @property {{ [_: StringAlias]: number } | null} SA - Accept + * @property {{ [_: `${number}`]: number } | null} IntT - Accept + * @property {{ [_: `${number}`]: number } | null} IntA - Accept + * @property {{ [_: string]: number } | null} VT - Reject + * @property {{ [_: string]: number } | null} VTPtr - Reject + * @property {{ [_: string]: number } | null} VPT - Reject + * @property {{ [_: string]: number } | null} VPTPtr - Reject + * @property {{ [_: ValueAlias]: number } | null} VA - Accept + * @property {{ [_: ValueAlias]: number } | null} VAPtr - Accept + * @property {{ [_: string]: number } | null} VPA - Accept, hide + * @property {{ [_: string]: number } | null} VPAPtr - Reject + * @property {{ [_: string]: number } | null} PT - Reject + * @property {{ [_: string]: number } | null} PTPtr - Reject + * @property {{ [_: string]: number } | null} PPT - Reject + * @property {{ [_: string]: number } | null} PPTPtr - Reject + * @property {{ [_: string]: number } | null} PA - Reject + * @property {{ [_: PointerAlias]: number } | null} PAPtr - Accept + * @property {{ [_: string]: number } | null} PPA - Accept, hide + * @property {{ [_: string]: number } | null} PPAPtr - Reject + * @property {{ [_: string]: number } | null} IT - Accept, hide + * @property {{ [_: string]: number } | null} ITPtr - Reject + * @property {{ [_: string]: number } | null} IPT - Reject + * @property {{ [_: string]: number } | null} IPTPtr - Reject + * @property {{ [_: string]: number } | null} IA - Accept, hide + * @property {{ [_: string]: number } | null} IAPtr - Reject + * @property {{ [_: string]: number } | null} IPA - Reject + * @property {{ [_: string]: number } | null} IPAPtr - Reject + * @property {{ [_: string]: number } | null} TPR - Soft reject + * @property {{ [_: string]: number } | null} TPRPtr - Soft reject + * @property {{ [_: string]: number } | null} TPS - Accept, hide + * @property {{ [_: string]: number } | null} TPSPtr - Soft reject + * @property {{ [_: string]: number } | null} TPT - Soft reject + * @property {{ [_: string]: number } | null} TPTPtr - Soft reject + * @property {{ [_: string]: number } | null} TPU - Accept, hide + * @property {{ [_: string]: number } | null} TPUPtr - Soft reject + * @property {{ [_: string]: number } | null} TPV - Accept, hide + * @property {{ [_: string]: number } | null} TPVPtr - Soft reject + * @property {{ [_: string]: number } | null} TPW - Soft reject + * @property {{ [_: string]: number } | null} TPWPtr - Accept, hide + * @property {{ [_: string]: number } | null} TPX - Accept, hide + * @property {{ [_: string]: number } | null} TPXPtr - Soft reject + * @property {{ [_: string]: number } | null} TPY - Accept, hide + * @property {{ [_: string]: number } | null} TPYPtr - Soft reject + * @property {{ [_: string]: number } | null} TPZ - Accept, hide + * @property {{ [_: string]: number } | null} TPZPtr - Soft reject + * @property {{ [_: string]: number } | null} GAR - Soft reject + * @property {{ [_: string]: number } | null} GARPtr - Soft reject + * @property {{ [_: string]: number } | null} GAS - Accept, hide + * @property {{ [_: string]: number } | null} GASPtr - Soft reject + * @property {{ [_: string]: number } | null} GAT - Soft reject + * @property {{ [_: string]: number } | null} GATPtr - Soft reject + * @property {{ [_: string]: number } | null} GAU - Accept, hide + * @property {{ [_: string]: number } | null} GAUPtr - Soft reject + * @property {{ [_: string]: number } | null} GAV - Accept, hide + * @property {{ [_: string]: number } | null} GAVPtr - Soft reject + * @property {{ [_: string]: number } | null} GAW - Soft reject + * @property {{ [_: string]: number } | null} GAWPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAX - Accept, hide + * @property {{ [_: string]: number } | null} GAXPtr - Soft reject + * @property {{ [_: string]: number } | null} GAY - Accept, hide + * @property {{ [_: string]: number } | null} GAYPtr - Soft reject + * @property {{ [_: string]: number } | null} GAZ - Accept, hide + * @property {{ [_: string]: number } | null} GAZPtr - Soft reject + * @property {{ [_: `${number}`]: number } | null} GACi - Accept, hide + * @property {{ [_: ComparableCstrAlias]: number } | null} GACV - Accept + * @property {{ [_: string]: number } | null} GACP - Reject + * @property {{ [_: string]: number } | null} GACiPtr - Reject + * @property {{ [_: string]: number } | null} GACVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GACPPtr - Accept, hide + * @property {{ [_: `${number}`]: number } | null} GABi - Accept, hide + * @property {{ [_: BasicCstrAlias]: number } | null} GABs - Accept + * @property {{ [_: string]: number } | null} GABiPtr - Reject + * @property {{ [_: string]: number } | null} GABT - Reject + * @property {{ [_: string]: number } | null} GABTPtr - Reject + * @property {{ [_: GoodTildeCstrAlias]: number } | null} GAGT - Accept + * @property {{ [_: string]: number } | null} GAGTPtr - Accept, hide + * @property {{ [_: NonBasicCstrAlias]: number } | null} GANBV - Accept + * @property {{ [_: string]: number } | null} GANBP - Accept, hide + * @property {{ [_: string]: number } | null} GANBVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GANBPPtr - Reject + * @property {{ [_: PointableCstrAlias]: number } | null} GAPlV1 - Accept + * @property {{ [_: PointableCstrAlias]: number } | null} GAPlV2 - Accept + * @property {{ [_: string]: number } | null} GAPlP1 - Reject + * @property {{ [_: PointableCstrAlias]: number } | null} GAPlP2 - Accept + * @property {{ [_: string]: number } | null} GAPlVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAPlPPtr - Accept, hide + * @property {{ [_: `${number}`]: number } | null} GAMi - Accept, hide + * @property {{ [_: MixedCstrAlias]: number } | null} GAMS - Accept + * @property {{ [_: MixedCstrAlias]: number } | null} GAMV - Accept + * @property {{ [_: string]: number } | null} GAMSPtr - Reject + * @property {{ [_: string]: number } | null} GAMVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAII - Accept, hide + * @property {{ [_: InterfaceCstrAlias]: number } | null} GAIV - Accept + * @property {{ [_: string]: number } | null} GAIP - Accept, hide + * @property {{ [_: string]: number } | null} GAIIPtr - Reject + * @property {{ [_: string]: number } | null} GAIVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAIPPtr - Reject + * @property {{ [_: string]: number } | null} GAPrV - Accept, hide + * @property {{ [_: string]: number } | null} GAPrP - Accept, hide + * @property {{ [_: string]: number } | null} GAPrVPtr - Reject + * @property {{ [_: string]: number } | null} GAPrPPtr - Reject + */ + +/** + * @template X + * @typedef {X} MixedCstrAlias + */ + +/** + * @template V + * @typedef {V} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {W} PointableCstrAlias + */ + +/** + * @typedef {PointerTextMarshaler} PointerAlias + */ + +/** + * @typedef {string} PointerTextMarshaler + */ + +/** + * @typedef {string} StringAlias + */ + +/** + * @typedef {string} StringType + */ + +/** + * @typedef {ValueTextMarshaler} ValueAlias + */ + +/** + * @typedef {string} ValueTextMarshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js new file mode 100644 index 000000000..810db1875 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js @@ -0,0 +1,18 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>>} + */ +export function Method() { + return $Call.ByID(4021345184); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js new file mode 100644 index 000000000..0f2edd9c7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js @@ -0,0 +1,124 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +import * as $models from "./models.js"; + +/** + * any + * @typedef {$models.AliasJsonMarshaler} AliasJsonMarshaler + */ + +/** + * any + * @typedef {$models.AliasMarshaler} AliasMarshaler + */ + +/** + * struct{} + * @typedef {$models.AliasNonMarshaler} AliasNonMarshaler + */ + +/** + * string + * @typedef {$models.AliasTextMarshaler} AliasTextMarshaler + */ + +/** + * @typedef {$models.Data} Data + */ + +/** + * any + * @typedef {$models.ImplicitJsonButText} ImplicitJsonButText + */ + +/** + * any + * @typedef {$models.ImplicitJsonMarshaler} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitMarshaler} ImplicitMarshaler + */ + +/** + * string + * @typedef {$models.ImplicitNonJson} ImplicitNonJson + */ + +/** + * class{ Marshaler, TextMarshaler } + * @typedef {$models.ImplicitNonMarshaler} ImplicitNonMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitNonText} ImplicitNonText + */ + +/** + * any + * @typedef {$models.ImplicitTextButJson} ImplicitTextButJson + */ + +/** + * string + * @typedef {$models.ImplicitTextMarshaler} ImplicitTextMarshaler + */ + +/** + * class {} + * @typedef {$models.NonMarshaler} NonMarshaler + */ + +/** + * any + * @typedef {$models.PointerJsonMarshaler} PointerJsonMarshaler + */ + +/** + * any + * @typedef {$models.PointerMarshaler} PointerMarshaler + */ + +/** + * string + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingJsonMarshaler} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingMarshaler} UnderlyingMarshaler + */ + +/** + * string + * @typedef {$models.UnderlyingTextMarshaler} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {$models.ValueJsonMarshaler} ValueJsonMarshaler + */ + +/** + * any + * @typedef {$models.ValueMarshaler} ValueMarshaler + */ + +/** + * string + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js new file mode 100644 index 000000000..a956da60f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js @@ -0,0 +1,186 @@ +// @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 * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + * @typedef {any} AliasJsonMarshaler + */ + +/** + * any + * @typedef {any} AliasMarshaler + */ + +/** + * struct{} + * @typedef { { + * } } AliasNonMarshaler + */ + +/** + * string + * @typedef {string} AliasTextMarshaler + */ + +/** + * @typedef {Object} Data + * @property {NonMarshaler} NM + * @property {NonMarshaler | null} NMPtr - NonMarshaler | null + * @property {ValueJsonMarshaler} VJM + * @property {ValueJsonMarshaler | null} VJMPtr - ValueJsonMarshaler | null + * @property {PointerJsonMarshaler} PJM + * @property {PointerJsonMarshaler | null} PJMPtr - PointerJsonMarshaler | null + * @property {ValueTextMarshaler} VTM + * @property {ValueTextMarshaler | null} VTMPtr - ValueTextMarshaler | null + * @property {PointerTextMarshaler} PTM + * @property {PointerTextMarshaler | null} PTMPtr - PointerTextMarshaler | null + * @property {ValueMarshaler} VM + * @property {ValueMarshaler | null} VMPtr - ValueMarshaler | null + * @property {PointerMarshaler} PM + * @property {PointerMarshaler | null} PMPtr - PointerMarshaler | null + * @property {UnderlyingJsonMarshaler} UJM + * @property {UnderlyingJsonMarshaler | null} UJMPtr - UnderlyingJsonMarshaler | null + * @property {UnderlyingTextMarshaler} UTM + * @property {UnderlyingTextMarshaler | null} UTMPtr - UnderlyingTextMarshaler | null + * @property {UnderlyingMarshaler} UM + * @property {UnderlyingMarshaler | null} UMPtr - UnderlyingMarshaler | null + * @property {any} JM - any + * @property {any | null} JMPtr - any | null + * @property {string} TM - string + * @property {string | null} TMPtr - string | null + * @property {any} CJM - any + * @property {any | null} CJMPtr - any | null + * @property {string} CTM - string + * @property {string | null} CTMPtr - string | null + * @property {any} CM - any + * @property {any | null} CMPtr - any | null + * @property {AliasNonMarshaler} ANM + * @property {AliasNonMarshaler | null} ANMPtr - AliasNonMarshaler | null + * @property {AliasJsonMarshaler} AJM + * @property {AliasJsonMarshaler | null} AJMPtr - AliasJsonMarshaler | null + * @property {AliasTextMarshaler} ATM + * @property {AliasTextMarshaler | null} ATMPtr - AliasTextMarshaler | null + * @property {AliasMarshaler} AM + * @property {AliasMarshaler | null} AMPtr - AliasMarshaler | null + * @property {ImplicitJsonMarshaler} ImJM + * @property {ImplicitJsonMarshaler | null} ImJMPtr - ImplicitJsonMarshaler | null + * @property {ImplicitTextMarshaler} ImTM + * @property {ImplicitTextMarshaler | null} ImTMPtr - ImplicitTextMarshaler | null + * @property {ImplicitMarshaler} ImM + * @property {ImplicitMarshaler | null} ImMPtr - ImplicitMarshaler | null + * @property {ImplicitNonJson} ImNJ + * @property {ImplicitNonJson | null} ImNJPtr - ImplicitNonJson | null + * @property {ImplicitNonText} ImNT + * @property {ImplicitNonText | null} ImNTPtr - ImplicitNonText | null + * @property {ImplicitNonMarshaler} ImNM + * @property {ImplicitNonMarshaler | null} ImNMPtr - ImplicitNonMarshaler | null + * @property {ImplicitJsonButText} ImJbT + * @property {ImplicitJsonButText | null} ImJbTPtr - ImplicitJsonButText | null + * @property {ImplicitTextButJson} ImTbJ + * @property {ImplicitTextButJson | null} ImTbJPtr - ImplicitTextButJson | null + */ + +/** + * any + * @typedef {any} ImplicitJsonButText + */ + +/** + * any + * @typedef {any} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {any} ImplicitMarshaler + */ + +/** + * string + * @typedef {string} ImplicitNonJson + */ + +/** + * class{ Marshaler, TextMarshaler } + * @typedef {Object} ImplicitNonMarshaler + * @property {json$0.Marshaler} Marshaler + * @property {encoding$0.TextMarshaler} TextMarshaler + */ + +/** + * any + * @typedef {any} ImplicitNonText + */ + +/** + * any + * @typedef {any} ImplicitTextButJson + */ + +/** + * string + * @typedef {string} ImplicitTextMarshaler + */ + +/** + * class {} + * @typedef { { + * } } NonMarshaler + */ + +/** + * any + * @typedef {any} PointerJsonMarshaler + */ + +/** + * any + * @typedef {any} PointerMarshaler + */ + +/** + * string + * @typedef {string} PointerTextMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingMarshaler + */ + +/** + * string + * @typedef {string} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {any} ValueJsonMarshaler + */ + +/** + * any + * @typedef {any} ValueMarshaler + */ + +/** + * string + * @typedef {string} ValueTextMarshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js new file mode 100644 index 000000000..5ba9fe470 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js @@ -0,0 +1,18 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Data>} + */ +export function Method() { + return $Call.ByID(4021345184); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js new file mode 100644 index 000000000..0b7f42650 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +import * as $models from "./models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + * @template How + * @typedef {$models.HowDifferent} HowDifferent + */ + +/** + * Impersonator gets their fields from other people. + * @typedef {$models.Impersonator} Impersonator + */ + +/** + * Person is not a number. + * @typedef {$models.Person} Person + */ + +/** + * PrivatePerson gets their fields from hidden sources. + * @typedef {$models.PrivatePerson} PrivatePerson + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js new file mode 100644 index 000000000..36f231303 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js @@ -0,0 +1,44 @@ +// @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 * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + * @template How + * @typedef {Object} HowDifferent + * @property {string} Name - They have a name as well. + * @property {({ [_: string]: How } | null)[] | null} Differences - But they may have many differences. + */ + +/** + * Impersonator gets their fields from other people. + * @typedef {other$0.OtherPerson} Impersonator + */ + +/** + * Person is not a number. + * @typedef {Object} Person + * @property {string} Name - They have a name. + * @property {Impersonator[]} Friends - Exactly 4 sketchy friends. + */ + +/** + * PrivatePerson gets their fields from hidden sources. + * @typedef {personImpl} PrivatePerson + */ + +/** + * @typedef {Object} personImpl + * @property {string} Nickname - Nickname conceals a person's identity. + * @property {string} Name - They have a name. + * @property {Impersonator[]} Friends - Exactly 4 sketchy friends. + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js new file mode 100644 index 000000000..33246d35e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js @@ -0,0 +1,16 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +import * as $models from "./models.js"; + +/** + * OtherPerson is like a person, but different. + * @template T + * @typedef {$models.OtherPerson} OtherPerson + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js new file mode 100644 index 000000000..63a2ee722 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherPerson is like a person, but different. + * @template T + * @typedef {Object} OtherPerson + * @property {string} Name - They have a name as well. + * @property {T[] | null} Differences - But they may have many differences. + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js new file mode 100644 index 000000000..d647deeb4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(3606939272); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js new file mode 100644 index 000000000..dca6f4e6c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByID(2124352079); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(4281222271); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js new file mode 100644 index 000000000..19bdcac29 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(3566862802); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js new file mode 100644 index 000000000..f50558b8c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByID(2590614085); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByID(773650321); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js new file mode 100644 index 000000000..3597b6460 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} $0 + * @returns {$CancellablePromise} + */ +export function Greet($0) { + return $Call.ByID(1411160069, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js new file mode 100644 index 000000000..734fb02e7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js new file mode 100644 index 000000000..9343d5531 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js new file mode 100644 index 000000000..b4f3f039c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js new file mode 100644 index 000000000..9343d5531 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js new file mode 100644 index 000000000..b4f3f039c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js new file mode 100644 index 000000000..40d2245b9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js new file mode 100644 index 000000000..41b452f57 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js @@ -0,0 +1,17 @@ +// @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 * as services$0 from "./services/models.js"; + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {services$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js new file mode 100644 index 000000000..7a4789f75 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(3568225479); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js new file mode 100644 index 000000000..ddd5e8916 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js @@ -0,0 +1,360 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByID(3862002418, $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByID(2424639793, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByID(3132595881, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByID(3306292566, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByID(1754277916, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByID(1909469092, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByID(4251088558, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByID(1343888303, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByID(2205561041, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByID(572240879, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByID(2189402897, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByID(642881729, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByID(1066151743, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByID(2718999663, $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByID(2386486356, $in); +} + +/** + * @param {{ [_: `${number}`]: number | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByID(2163571325, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByID(2900172572, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] | null } | null>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByID(881980169, $in); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByID(1075577233); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByID(3589606958, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByID(224675106, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByID(2124953624, $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByID(3516977899, $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByID(229603958, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByID(3678582682, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByID(319259595, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByID(383995060, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByID(1091960237, $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByID(3835643147, $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByID(2447692557, $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByID(2943477349, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByID(3401034892, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByID(1236957573, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByID(1160383782, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByID(1739300671, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByID(793803239, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByID(1403757716, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByID(2988345717, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByID(518250834, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByID(2836661285, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByID(1367187362, $in); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js new file mode 100644 index 000000000..ec6136e27 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {Person | null} Parent + * @property {{"Age": number, "Address": {"Street": string}}} Details + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js new file mode 100644 index 000000000..ddd5e8916 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js @@ -0,0 +1,360 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByID(3862002418, $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByID(2424639793, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByID(3132595881, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByID(3306292566, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByID(1754277916, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByID(1909469092, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByID(4251088558, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByID(1343888303, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByID(2205561041, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByID(572240879, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByID(2189402897, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByID(642881729, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByID(1066151743, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByID(2718999663, $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByID(2386486356, $in); +} + +/** + * @param {{ [_: `${number}`]: number | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByID(2163571325, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByID(2900172572, $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] | null } | null>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByID(881980169, $in); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByID(1075577233); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByID(3589606958, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByID(224675106, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByID(2124953624, $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByID(3516977899, $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByID(229603958, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByID(3678582682, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByID(319259595, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByID(383995060, $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByID(1091960237, $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByID(3835643147, $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByID(2447692557, $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByID(2943477349, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByID(3401034892, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByID(1236957573, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByID(1160383782, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByID(1739300671, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByID(793803239, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByID(1403757716, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByID(2988345717, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByID(518250834, $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByID(2836661285, $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByID(1367187362, $in); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js new file mode 100644 index 000000000..ec6136e27 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {Person | null} Parent + * @property {{"Age": number, "Address": {"Street": string}}} Details + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js new file mode 100644 index 000000000..6c1448d81 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js new file mode 100644 index 000000000..6c1448d81 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js new file mode 100644 index 000000000..40d2245b9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js new file mode 100644 index 000000000..977600693 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js @@ -0,0 +1,16 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * Person is a person! + * They have a name and an address + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js new file mode 100644 index 000000000..ab5cea255 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js @@ -0,0 +1,19 @@ +// @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 * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + * @typedef {Object} Person + * @property {string} Name + * @property {services$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js new file mode 100644 index 000000000..3c9c56546 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByID(1491748400); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/warnings.log b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=false/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/index.js new file mode 100644 index 000000000..cf48d86db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/index.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {$models.TextMarshaler} TextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/json/index.js new file mode 100644 index 000000000..22f1fd904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/json/index.js @@ -0,0 +1,11 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {$models.Marshaler} Marshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/json/models.js new file mode 100644 index 000000000..5dce4eea6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/json/models.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + * @typedef {any} Marshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/models.js new file mode 100644 index 000000000..db89bafbc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/encoding/models.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + * @typedef {any} TextMarshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js new file mode 100644 index 000000000..cc81267aa --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.js @@ -0,0 +1,70 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + * @param {$models.Alias} aliasValue + * @returns {$CancellablePromise<$models.Person>} + */ +export function Get(aliasValue) { + return $Call.ByName("main.GreetService.Get", aliasValue); +} + +/** + * Apparently, aliases are all the rage right now. + * @param {$models.AliasedPerson} p + * @returns {$CancellablePromise<$models.StrangelyAliasedPerson>} + */ +export function GetButAliased(p) { + return $Call.ByName("main.GreetService.GetButAliased", p); +} + +/** + * Get someone quite different. + * @returns {$CancellablePromise<$models.GenericPerson>} + */ +export function GetButDifferent() { + return $Call.ByName("main.GreetService.GetButDifferent"); +} + +/** + * @returns {$CancellablePromise} + */ +export function GetButForeignPrivateAlias() { + return $Call.ByName("main.GreetService.GetButForeignPrivateAlias"); +} + +/** + * @returns {$CancellablePromise<$models.AliasGroup>} + */ +export function GetButGenericAliases() { + return $Call.ByName("main.GreetService.GetButGenericAliases"); +} + +/** + * Greet a lot of unusual things. + * @param {$models.EmptyAliasStruct} $0 + * @param {$models.EmptyStruct} $1 + * @returns {$CancellablePromise<$models.AliasStruct>} + */ +export function Greet($0, $1) { + return $Call.ByName("main.GreetService.Greet", $0, $1); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js new file mode 100644 index 000000000..5a5c62644 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.js @@ -0,0 +1,96 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * A nice type Alias. + * @typedef {$models.Alias} Alias + */ + +/** + * A class whose fields have various aliased types. + * @typedef {$models.AliasGroup} AliasGroup + */ + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {$models.AliasStruct} AliasStruct + */ + +/** + * A class alias. + * @typedef {$models.AliasedPerson} AliasedPerson + */ + +/** + * An empty struct alias. + * @typedef {$models.EmptyAliasStruct} EmptyAliasStruct + */ + +/** + * An empty struct. + * @typedef {$models.EmptyStruct} EmptyStruct + */ + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {$models.GenericAlias} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {$models.GenericMapAlias} GenericMapAlias + */ + +/** + * A generic struct containing an alias. + * @template T + * @typedef {$models.GenericPerson} GenericPerson + */ + +/** + * A generic alias that wraps a generic struct. + * @template T + * @typedef {$models.GenericPersonAlias} GenericPersonAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {$models.GenericPtrAlias} GenericPtrAlias + */ + +/** + * An alias that wraps a class through a non-typeparam alias. + * @typedef {$models.IndirectPersonAlias} IndirectPersonAlias + */ + +/** + * Another struct alias. + * @typedef {$models.OtherAliasStruct} OtherAliasStruct + */ + +/** + * A non-generic struct containing an alias. + * @typedef {$models.Person} Person + */ + +/** + * Another class alias, but ordered after its aliased class. + * @typedef {$models.StrangelyAliasedPerson} StrangelyAliasedPerson + */ + +/** + * An alias that wraps a class through a typeparam alias. + * @typedef {$models.TPIndirectPersonAlias} TPIndirectPersonAlias + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js new file mode 100644 index 000000000..1ba2af395 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.js @@ -0,0 +1,112 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * A nice type Alias. + * @typedef {number} Alias + */ + +/** + * A class whose fields have various aliased types. + * @typedef {Object} AliasGroup + * @property {GenericAlias} GAi + * @property {GenericAlias>} GAP + * @property {GenericPtrAlias} GPAs + * @property {GenericPtrAlias>} GPAP + * @property {GenericMapAlias} GMA + * @property {GenericPersonAlias} GPA + * @property {IndirectPersonAlias} IPA + * @property {TPIndirectPersonAlias} TPIPA + */ + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + * @typedef {Object} AliasStruct + * @property {number[] | null} Foo - A field with a comment. + * @property {string} [Bar] - Definitely not Foo. + * @property {string} [Baz] - Definitely not Foo. + * @property {OtherAliasStruct} Other - A nested alias struct. + */ + +/** + * A class alias. + * @typedef {Person} AliasedPerson + */ + +/** + * An empty struct alias. + * @typedef { { + * } } EmptyAliasStruct + */ + +/** + * An empty struct. + * @typedef { { + * } } EmptyStruct + */ + +/** + * A generic alias that forwards to a type parameter. + * @template T + * @typedef {T} GenericAlias + */ + +/** + * A generic alias that wraps a map. + * @template T,U + * @typedef {{ [_: string]: U } | null} GenericMapAlias + */ + +/** + * A generic struct containing an alias. + * @template T + * @typedef {Object} GenericPerson + * @property {T} Name + * @property {Alias} AliasedField + */ + +/** + * A generic alias that wraps a generic struct. + * @template T + * @typedef {GenericPerson[] | null>} GenericPersonAlias + */ + +/** + * A generic alias that wraps a pointer type. + * @template T + * @typedef {GenericAlias | null} GenericPtrAlias + */ + +/** + * An alias that wraps a class through a non-typeparam alias. + * @typedef {GenericPersonAlias} IndirectPersonAlias + */ + +/** + * Another struct alias. + * @typedef {Object} OtherAliasStruct + * @property {number[] | null} NoMoreIdeas + */ + +/** + * A non-generic struct containing an alias. + * @typedef {Object} Person + * @property {string} Name - The Person's name. + * @property {Alias} AliasedField - A random alias field. + */ + +/** + * Another class alias, but ordered after its aliased class. + * @typedef {Person} StrangelyAliasedPerson + */ + +/** + * An alias that wraps a class through a typeparam alias. + * @typedef {GenericAlias>} TPIndirectPersonAlias + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js new file mode 100644 index 000000000..f6c839720 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js new file mode 100644 index 000000000..f62552e8a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service7.TestMethod"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js new file mode 100644 index 000000000..244e74afe --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function TestMethod2() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service9.TestMethod2"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js new file mode 100644 index 000000000..e146c3669 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {$models.Person} person + * @param {$models.Embedded1} emb + * @returns {$CancellablePromise} + */ +export function Greet(person, emb) { + return $Call.ByName("main.GreetService.Greet", person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js new file mode 100644 index 000000000..f21130b86 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.js @@ -0,0 +1,27 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Title +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Embedded1} Embedded1 + */ + +/** + * @typedef {$models.Embedded3} Embedded3 + */ + +/** + * Person represents a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js new file mode 100644 index 000000000..b30630777 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.js @@ -0,0 +1,64 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Embedded1 + * @property {number} Friends - Friends should be shadowed in Person by a field of lesser depth + * @property {number} Vanish - Vanish should be omitted from Person because there is another field with same depth and no tag + * @property {string} StillThere - StillThere should be shadowed in Person by other field with same depth and a json tag + * @property {`${boolean}`} NamingThingsIsHard - NamingThingsIsHard is a law of programming + */ + +/** + * @typedef {string} Embedded3 + */ + +/** + * Person represents a person + * @typedef { { + * "Titles"?: Title[] | null, + * "Names": string[] | null, + * "Partner": Person | null, + * "Friends": (Person | null)[] | null, + * "NamingThingsIsHard": `${boolean}`, + * "StillThere": Embedded3 | null, + * "-": number, + * "Embedded3": Embedded3, + * "StrangerNumber": `${number}`, + * "StrangestString"?: `"${string}"`, + * "StringStrangest"?: `"${string}"`, + * "emb4"?: embedded4, + * } } Person + */ + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +/** + * @typedef {Object} embedded4 + * @property {`${boolean}`} NamingThingsIsHard - NamingThingsIsHard is a law of programming + * @property {boolean} Friends - Friends should not be shadowed in Person as embedded4 is not embedded from encoding/json's point of view; however, it should be shadowed in Embedded1 + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js new file mode 100644 index 000000000..03ef1f90f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + * @param {string} str + * @param {$models.Person[] | null} people + * @param {{"AnotherCount": number, "AnotherOne": $models.Person | null}} $2 + * @param {{ [_: `${number}`]: boolean | null } | null} assoc + * @param {(number | null)[] | null} $4 + * @param {string[]} other + * @returns {$CancellablePromise<[$models.Person, any, number[] | null]>} + */ +export function Greet(str, people, $2, assoc, $4, ...other) { + return $Call.ByName("main.GreetService.Greet", str, people, $2, assoc, $4, other); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js new file mode 100644 index 000000000..88af1203b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * Person represents a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js new file mode 100644 index 000000000..035bc0792 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.js @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Person represents a person + * @typedef {Object} Person + * @property {string} Name + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js new file mode 100644 index 000000000..8203e4168 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.StructA, $models.StructC]>} + */ +export function MakeCycles() { + return $Call.ByName("main.GreetService.MakeCycles"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js new file mode 100644 index 000000000..9b49a9172 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.js @@ -0,0 +1,22 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.StructA} StructA + */ + +/** + * @typedef {$models.StructC} StructC + */ + +/** + * @typedef {$models.StructE} StructE + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js new file mode 100644 index 000000000..50e0d0fc0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} StructA + * @property {structB | null} B + */ + +/** + * @typedef {Object} StructC + * @property {structD} D + */ + +/** + * @typedef { { + * } } StructE + */ + +/** + * @typedef {Object} structB + * @property {StructA | null} A + */ + +/** + * @typedef {Object} structD + * @property {StructE} E + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js new file mode 100644 index 000000000..623e88035 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + * @returns {$CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]>} + */ +export function MakeCycles() { + return $Call.ByName("main.GreetService.MakeCycles"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js new file mode 100644 index 000000000..9fc31bf7c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.js @@ -0,0 +1,23 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Alias} Alias + */ + +/** + * @typedef {$models.Cyclic} Cyclic + */ + +/** + * @template T + * @typedef {$models.GenericCyclic} GenericCyclic + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js new file mode 100644 index 000000000..2413995ac --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Cyclic | null} Alias + */ + +/** + * @typedef {({ [_: string]: Alias } | null)[] | null} Cyclic + */ + +/** + * @template T + * @typedef {{"X": GenericCyclic | null, "Y": T[] | null}[] | null} GenericCyclic + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js new file mode 100644 index 000000000..40d68bf85 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Interfaces!"); +console.log("Hello JS!"); +console.log("Hello JS Interfaces!"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js new file mode 100644 index 000000000..374d13203 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.InternalModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByName("main.InternalService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js new file mode 100644 index 000000000..a4c233c8c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.js @@ -0,0 +1,19 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal model. + * @typedef {Object} InternalModel + * @property {string} Field + */ + +/** + * An unexported model. + * @typedef {Object} unexportedModel + * @property {string} Field + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js new file mode 100644 index 000000000..c93da8f05 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.js @@ -0,0 +1,9 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Dummy} Dummy + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js new file mode 100644 index 000000000..6b6f5401f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef { { + * } } Dummy + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js new file mode 100644 index 000000000..8332051eb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.js @@ -0,0 +1,35 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +/** + * @param {string} $0 + * @returns {$CancellablePromise} + */ +function InternalMethod($0) { + return $Call.ByName("main.Service.InternalMethod", $0); +} + +/** + * @param {otherpackage$0.Dummy} $0 + * @returns {$CancellablePromise} + */ +export function VisibleMethod($0) { + return $Call.ByName("main.Service.VisibleMethod", $0); +} + +/** + * @param {string} arg + * @returns {Promise} + */ +export async function CustomMethod(arg) { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js new file mode 100644 index 000000000..442f20472 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js new file mode 100644 index 000000000..b2f9c5edb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_j.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ji.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ji.js new file mode 100644 index 000000000..36e28f09b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ji.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("JS Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js new file mode 100644 index 000000000..1fe8a0f69 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {$models.unexportedModel} $0 + * @returns {$CancellablePromise} + */ +export function Method($0) { + return $Call.ByName("main.unexportedService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js new file mode 100644 index 000000000..3d245f10c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.js @@ -0,0 +1,53 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Comment 1. + * @returns {$CancellablePromise} + */ +export function Method1() { + return $Call.ByName("main.GreetService.Method1"); +} + +/** + * Comment 2. + * @returns {$CancellablePromise} + */ +export function Method2() { + return $Call.ByName("main.GreetService.Method2"); +} + +/** + * Comment 3a. + * Comment 3b. + * @returns {$CancellablePromise} + */ +export function Method3() { + return $Call.ByName("main.GreetService.Method3"); +} + +/** + * Comment 4. + * @returns {$CancellablePromise} + */ +export function Method4() { + return $Call.ByName("main.GreetService.Method4"); +} + +/** + * Comment 5. + * @returns {$CancellablePromise} + */ +export function Method5() { + return $Call.ByName("main.GreetService.Method5"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js new file mode 100644 index 000000000..fa5b9a262 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.js @@ -0,0 +1,35 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {$models.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByName("main.GreetService.Greet", name, title); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js new file mode 100644 index 000000000..649d8d016 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Title +} from "./models.js"; + +import * as $models from "./models.js"; + +/** + * Person represents a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js new file mode 100644 index 000000000..e7c70729c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.js @@ -0,0 +1,61 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Age is an integer with some predefined values + * @typedef {number} Age + */ + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + * @typedef {Object} Person + * @property {Title} Title + * @property {string} Name + * @property {Age} Age + */ + +/** + * Title is a title + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js new file mode 100644 index 000000000..2f953de7c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.js @@ -0,0 +1,26 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @param {services$0.Title} title + * @returns {$CancellablePromise} + */ +export function Greet(name, title) { + return $Call.ByName("main.GreetService.Greet", name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js new file mode 100644 index 000000000..089a8b685 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js new file mode 100644 index 000000000..e0e2d3014 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.js @@ -0,0 +1,27 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @readonly + * @enum {string} + */ +export const Title = { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero: "", + + /** + * Mister is a title + */ + Mister: "Mr", + Miss: "Miss", + Ms: "Ms", + Mrs: "Mrs", + Dr: "Dr", +}; + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js new file mode 100644 index 000000000..8ab5f3462 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js new file mode 100644 index 000000000..26922b7eb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * Person is a person + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js new file mode 100644 index 000000000..5743a9055 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.js @@ -0,0 +1,18 @@ +// @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 * as services$0 from "./services/models.js"; + +/** + * Person is a person + * @typedef {Object} Person + * @property {string} Name + * @property {services$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js new file mode 100644 index 000000000..a27b0fea8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js new file mode 100644 index 000000000..8ab5f3462 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js new file mode 100644 index 000000000..8a9890617 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.js @@ -0,0 +1,17 @@ +// @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 * as other$0 from "./services/other/models.js"; + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {other$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js new file mode 100644 index 000000000..98097e64f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js new file mode 100644 index 000000000..e2ba84581 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js new file mode 100644 index 000000000..bba7ea7ea --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js new file mode 100644 index 000000000..f89bfc417 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.js @@ -0,0 +1,30 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function GreetWithContext(name) { + return $Call.ByName("main.GreetService.GreetWithContext", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js new file mode 100644 index 000000000..bba7ea7ea --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js new file mode 100644 index 000000000..80fdcd24c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.js @@ -0,0 +1,98 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +import * as $models from "./models.js"; + +/** + * @template S + * @typedef {$models.BasicCstrAlias} BasicCstrAlias + */ + +/** + * @template R + * @typedef {$models.ComparableCstrAlias} ComparableCstrAlias + */ + +/** + * @typedef {$models.EmbeddedCustomInterface} EmbeddedCustomInterface + */ + +/** + * @typedef {$models.EmbeddedOriginalInterface} EmbeddedOriginalInterface + */ + +/** + * @typedef {$models.EmbeddedPointer} EmbeddedPointer + */ + +/** + * @typedef {$models.EmbeddedPointerPtr} EmbeddedPointerPtr + */ + +/** + * @typedef {$models.EmbeddedValue} EmbeddedValue + */ + +/** + * @typedef {$models.EmbeddedValuePtr} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {$models.GoodTildeCstrAlias} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {$models.InterfaceCstrAlias} InterfaceCstrAlias + */ + +/** + * @template R,S,T,U,V,W,X,Y,Z + * @typedef {$models.Maps} Maps + */ + +/** + * @template X + * @typedef {$models.MixedCstrAlias} MixedCstrAlias + */ + +/** + * @template V + * @typedef {$models.NonBasicCstrAlias} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {$models.PointableCstrAlias} PointableCstrAlias + */ + +/** + * @typedef {$models.PointerAlias} PointerAlias + */ + +/** + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * @typedef {$models.StringAlias} StringAlias + */ + +/** + * @typedef {$models.StringType} StringType + */ + +/** + * @typedef {$models.ValueAlias} ValueAlias + */ + +/** + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js new file mode 100644 index 000000000..cee61a5e4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.js @@ -0,0 +1,240 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @template S + * @typedef {S} BasicCstrAlias + */ + +/** + * @template R + * @typedef {R} ComparableCstrAlias + */ + +/** + * @typedef {string} EmbeddedCustomInterface + */ + +/** + * @typedef {string} EmbeddedOriginalInterface + */ + +/** + * @typedef {string} EmbeddedPointer + */ + +/** + * @typedef {string} EmbeddedPointerPtr + */ + +/** + * @typedef {string} EmbeddedValue + */ + +/** + * @typedef {string} EmbeddedValuePtr + */ + +/** + * @template U + * @typedef {U} GoodTildeCstrAlias + */ + +/** + * @template Y + * @typedef {Y} InterfaceCstrAlias + */ + +/** + * @template R,S,T,U,V,W,X,Y,Z + * @typedef {Object} Maps + * @property {{ [_: string]: number } | null} Bool - Reject + * @property {{ [_: `${number}`]: number } | null} Int - Accept + * @property {{ [_: `${number}`]: number } | null} Uint - Accept + * @property {{ [_: string]: number } | null} Float - Reject + * @property {{ [_: string]: number } | null} Complex - Reject + * @property {{ [_: `${number}`]: number } | null} Byte - Accept + * @property {{ [_: `${number}`]: number } | null} Rune - Accept + * @property {{ [_: string]: number } | null} String - Accept + * @property {{ [_: string]: number } | null} IntPtr - Reject + * @property {{ [_: string]: number } | null} UintPtr - Reject + * @property {{ [_: string]: number } | null} FloatPtr - Reject + * @property {{ [_: string]: number } | null} ComplexPtr - Reject + * @property {{ [_: string]: number } | null} StringPtr - Reject + * @property {{ [_: string]: number } | null} NTM - Reject + * @property {{ [_: string]: number } | null} NTMPtr - Reject + * @property {{ [_: ValueTextMarshaler]: number } | null} VTM - Accept + * @property {{ [_: ValueTextMarshaler]: number } | null} VTMPtr - Accept + * @property {{ [_: string]: number } | null} PTM - Reject + * @property {{ [_: PointerTextMarshaler]: number } | null} PTMPtr - Accept + * @property {{ [_: string]: number } | null} JTM - Accept, hide + * @property {{ [_: string]: number } | null} JTMPtr - Accept, hide + * @property {{ [_: string]: number } | null} A - Reject + * @property {{ [_: string]: number } | null} APtr - Reject + * @property {{ [_: string]: number } | null} TM - Accept, hide + * @property {{ [_: string]: number } | null} TMPtr - Reject + * @property {{ [_: string]: number } | null} CI - Accept, hide + * @property {{ [_: string]: number } | null} CIPtr - Reject + * @property {{ [_: string]: number } | null} EI - Accept, hide + * @property {{ [_: string]: number } | null} EIPtr - Reject + * @property {{ [_: EmbeddedValue]: number } | null} EV - Accept + * @property {{ [_: EmbeddedValue]: number } | null} EVPtr - Accept + * @property {{ [_: EmbeddedValuePtr]: number } | null} EVP - Accept + * @property {{ [_: EmbeddedValuePtr]: number } | null} EVPPtr - Accept + * @property {{ [_: string]: number } | null} EP - Reject + * @property {{ [_: EmbeddedPointer]: number } | null} EPPtr - Accept + * @property {{ [_: EmbeddedPointerPtr]: number } | null} EPP - Accept + * @property {{ [_: EmbeddedPointerPtr]: number } | null} EPPPtr - Accept + * @property {{ [_: EmbeddedCustomInterface]: number } | null} ECI - Accept + * @property {{ [_: EmbeddedCustomInterface]: number } | null} ECIPtr - Accept + * @property {{ [_: EmbeddedOriginalInterface]: number } | null} EOI - Accept + * @property {{ [_: EmbeddedOriginalInterface]: number } | null} EOIPtr - Accept + * @property {{ [_: string]: number } | null} WT - Reject + * @property {{ [_: string]: number } | null} WA - Reject + * @property {{ [_: StringType]: number } | null} ST - Accept + * @property {{ [_: StringAlias]: number } | null} SA - Accept + * @property {{ [_: `${number}`]: number } | null} IntT - Accept + * @property {{ [_: `${number}`]: number } | null} IntA - Accept + * @property {{ [_: string]: number } | null} VT - Reject + * @property {{ [_: string]: number } | null} VTPtr - Reject + * @property {{ [_: string]: number } | null} VPT - Reject + * @property {{ [_: string]: number } | null} VPTPtr - Reject + * @property {{ [_: ValueAlias]: number } | null} VA - Accept + * @property {{ [_: ValueAlias]: number } | null} VAPtr - Accept + * @property {{ [_: string]: number } | null} VPA - Accept, hide + * @property {{ [_: string]: number } | null} VPAPtr - Reject + * @property {{ [_: string]: number } | null} PT - Reject + * @property {{ [_: string]: number } | null} PTPtr - Reject + * @property {{ [_: string]: number } | null} PPT - Reject + * @property {{ [_: string]: number } | null} PPTPtr - Reject + * @property {{ [_: string]: number } | null} PA - Reject + * @property {{ [_: PointerAlias]: number } | null} PAPtr - Accept + * @property {{ [_: string]: number } | null} PPA - Accept, hide + * @property {{ [_: string]: number } | null} PPAPtr - Reject + * @property {{ [_: string]: number } | null} IT - Accept, hide + * @property {{ [_: string]: number } | null} ITPtr - Reject + * @property {{ [_: string]: number } | null} IPT - Reject + * @property {{ [_: string]: number } | null} IPTPtr - Reject + * @property {{ [_: string]: number } | null} IA - Accept, hide + * @property {{ [_: string]: number } | null} IAPtr - Reject + * @property {{ [_: string]: number } | null} IPA - Reject + * @property {{ [_: string]: number } | null} IPAPtr - Reject + * @property {{ [_: string]: number } | null} TPR - Soft reject + * @property {{ [_: string]: number } | null} TPRPtr - Soft reject + * @property {{ [_: string]: number } | null} TPS - Accept, hide + * @property {{ [_: string]: number } | null} TPSPtr - Soft reject + * @property {{ [_: string]: number } | null} TPT - Soft reject + * @property {{ [_: string]: number } | null} TPTPtr - Soft reject + * @property {{ [_: string]: number } | null} TPU - Accept, hide + * @property {{ [_: string]: number } | null} TPUPtr - Soft reject + * @property {{ [_: string]: number } | null} TPV - Accept, hide + * @property {{ [_: string]: number } | null} TPVPtr - Soft reject + * @property {{ [_: string]: number } | null} TPW - Soft reject + * @property {{ [_: string]: number } | null} TPWPtr - Accept, hide + * @property {{ [_: string]: number } | null} TPX - Accept, hide + * @property {{ [_: string]: number } | null} TPXPtr - Soft reject + * @property {{ [_: string]: number } | null} TPY - Accept, hide + * @property {{ [_: string]: number } | null} TPYPtr - Soft reject + * @property {{ [_: string]: number } | null} TPZ - Accept, hide + * @property {{ [_: string]: number } | null} TPZPtr - Soft reject + * @property {{ [_: string]: number } | null} GAR - Soft reject + * @property {{ [_: string]: number } | null} GARPtr - Soft reject + * @property {{ [_: string]: number } | null} GAS - Accept, hide + * @property {{ [_: string]: number } | null} GASPtr - Soft reject + * @property {{ [_: string]: number } | null} GAT - Soft reject + * @property {{ [_: string]: number } | null} GATPtr - Soft reject + * @property {{ [_: string]: number } | null} GAU - Accept, hide + * @property {{ [_: string]: number } | null} GAUPtr - Soft reject + * @property {{ [_: string]: number } | null} GAV - Accept, hide + * @property {{ [_: string]: number } | null} GAVPtr - Soft reject + * @property {{ [_: string]: number } | null} GAW - Soft reject + * @property {{ [_: string]: number } | null} GAWPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAX - Accept, hide + * @property {{ [_: string]: number } | null} GAXPtr - Soft reject + * @property {{ [_: string]: number } | null} GAY - Accept, hide + * @property {{ [_: string]: number } | null} GAYPtr - Soft reject + * @property {{ [_: string]: number } | null} GAZ - Accept, hide + * @property {{ [_: string]: number } | null} GAZPtr - Soft reject + * @property {{ [_: `${number}`]: number } | null} GACi - Accept, hide + * @property {{ [_: ComparableCstrAlias]: number } | null} GACV - Accept + * @property {{ [_: string]: number } | null} GACP - Reject + * @property {{ [_: string]: number } | null} GACiPtr - Reject + * @property {{ [_: string]: number } | null} GACVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GACPPtr - Accept, hide + * @property {{ [_: `${number}`]: number } | null} GABi - Accept, hide + * @property {{ [_: BasicCstrAlias]: number } | null} GABs - Accept + * @property {{ [_: string]: number } | null} GABiPtr - Reject + * @property {{ [_: string]: number } | null} GABT - Reject + * @property {{ [_: string]: number } | null} GABTPtr - Reject + * @property {{ [_: GoodTildeCstrAlias]: number } | null} GAGT - Accept + * @property {{ [_: string]: number } | null} GAGTPtr - Accept, hide + * @property {{ [_: NonBasicCstrAlias]: number } | null} GANBV - Accept + * @property {{ [_: string]: number } | null} GANBP - Accept, hide + * @property {{ [_: string]: number } | null} GANBVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GANBPPtr - Reject + * @property {{ [_: PointableCstrAlias]: number } | null} GAPlV1 - Accept + * @property {{ [_: PointableCstrAlias]: number } | null} GAPlV2 - Accept + * @property {{ [_: string]: number } | null} GAPlP1 - Reject + * @property {{ [_: PointableCstrAlias]: number } | null} GAPlP2 - Accept + * @property {{ [_: string]: number } | null} GAPlVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAPlPPtr - Accept, hide + * @property {{ [_: `${number}`]: number } | null} GAMi - Accept, hide + * @property {{ [_: MixedCstrAlias]: number } | null} GAMS - Accept + * @property {{ [_: MixedCstrAlias]: number } | null} GAMV - Accept + * @property {{ [_: string]: number } | null} GAMSPtr - Reject + * @property {{ [_: string]: number } | null} GAMVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAII - Accept, hide + * @property {{ [_: InterfaceCstrAlias]: number } | null} GAIV - Accept + * @property {{ [_: string]: number } | null} GAIP - Accept, hide + * @property {{ [_: string]: number } | null} GAIIPtr - Reject + * @property {{ [_: string]: number } | null} GAIVPtr - Accept, hide + * @property {{ [_: string]: number } | null} GAIPPtr - Reject + * @property {{ [_: string]: number } | null} GAPrV - Accept, hide + * @property {{ [_: string]: number } | null} GAPrP - Accept, hide + * @property {{ [_: string]: number } | null} GAPrVPtr - Reject + * @property {{ [_: string]: number } | null} GAPrPPtr - Reject + */ + +/** + * @template X + * @typedef {X} MixedCstrAlias + */ + +/** + * @template V + * @typedef {V} NonBasicCstrAlias + */ + +/** + * @template W + * @typedef {W} PointableCstrAlias + */ + +/** + * @typedef {PointerTextMarshaler} PointerAlias + */ + +/** + * @typedef {string} PointerTextMarshaler + */ + +/** + * @typedef {string} StringAlias + */ + +/** + * @typedef {string} StringType + */ + +/** + * @typedef {ValueTextMarshaler} ValueAlias + */ + +/** + * @typedef {string} ValueTextMarshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js new file mode 100644 index 000000000..e207d968c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.js @@ -0,0 +1,18 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>>} + */ +export function Method() { + return $Call.ByName("main.Service.Method"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js new file mode 100644 index 000000000..0f2edd9c7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.js @@ -0,0 +1,124 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +import * as $models from "./models.js"; + +/** + * any + * @typedef {$models.AliasJsonMarshaler} AliasJsonMarshaler + */ + +/** + * any + * @typedef {$models.AliasMarshaler} AliasMarshaler + */ + +/** + * struct{} + * @typedef {$models.AliasNonMarshaler} AliasNonMarshaler + */ + +/** + * string + * @typedef {$models.AliasTextMarshaler} AliasTextMarshaler + */ + +/** + * @typedef {$models.Data} Data + */ + +/** + * any + * @typedef {$models.ImplicitJsonButText} ImplicitJsonButText + */ + +/** + * any + * @typedef {$models.ImplicitJsonMarshaler} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitMarshaler} ImplicitMarshaler + */ + +/** + * string + * @typedef {$models.ImplicitNonJson} ImplicitNonJson + */ + +/** + * class{ Marshaler, TextMarshaler } + * @typedef {$models.ImplicitNonMarshaler} ImplicitNonMarshaler + */ + +/** + * any + * @typedef {$models.ImplicitNonText} ImplicitNonText + */ + +/** + * any + * @typedef {$models.ImplicitTextButJson} ImplicitTextButJson + */ + +/** + * string + * @typedef {$models.ImplicitTextMarshaler} ImplicitTextMarshaler + */ + +/** + * class {} + * @typedef {$models.NonMarshaler} NonMarshaler + */ + +/** + * any + * @typedef {$models.PointerJsonMarshaler} PointerJsonMarshaler + */ + +/** + * any + * @typedef {$models.PointerMarshaler} PointerMarshaler + */ + +/** + * string + * @typedef {$models.PointerTextMarshaler} PointerTextMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingJsonMarshaler} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {$models.UnderlyingMarshaler} UnderlyingMarshaler + */ + +/** + * string + * @typedef {$models.UnderlyingTextMarshaler} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {$models.ValueJsonMarshaler} ValueJsonMarshaler + */ + +/** + * any + * @typedef {$models.ValueMarshaler} ValueMarshaler + */ + +/** + * string + * @typedef {$models.ValueTextMarshaler} ValueTextMarshaler + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js new file mode 100644 index 000000000..a956da60f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.js @@ -0,0 +1,186 @@ +// @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 * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + * @typedef {any} AliasJsonMarshaler + */ + +/** + * any + * @typedef {any} AliasMarshaler + */ + +/** + * struct{} + * @typedef { { + * } } AliasNonMarshaler + */ + +/** + * string + * @typedef {string} AliasTextMarshaler + */ + +/** + * @typedef {Object} Data + * @property {NonMarshaler} NM + * @property {NonMarshaler | null} NMPtr - NonMarshaler | null + * @property {ValueJsonMarshaler} VJM + * @property {ValueJsonMarshaler | null} VJMPtr - ValueJsonMarshaler | null + * @property {PointerJsonMarshaler} PJM + * @property {PointerJsonMarshaler | null} PJMPtr - PointerJsonMarshaler | null + * @property {ValueTextMarshaler} VTM + * @property {ValueTextMarshaler | null} VTMPtr - ValueTextMarshaler | null + * @property {PointerTextMarshaler} PTM + * @property {PointerTextMarshaler | null} PTMPtr - PointerTextMarshaler | null + * @property {ValueMarshaler} VM + * @property {ValueMarshaler | null} VMPtr - ValueMarshaler | null + * @property {PointerMarshaler} PM + * @property {PointerMarshaler | null} PMPtr - PointerMarshaler | null + * @property {UnderlyingJsonMarshaler} UJM + * @property {UnderlyingJsonMarshaler | null} UJMPtr - UnderlyingJsonMarshaler | null + * @property {UnderlyingTextMarshaler} UTM + * @property {UnderlyingTextMarshaler | null} UTMPtr - UnderlyingTextMarshaler | null + * @property {UnderlyingMarshaler} UM + * @property {UnderlyingMarshaler | null} UMPtr - UnderlyingMarshaler | null + * @property {any} JM - any + * @property {any | null} JMPtr - any | null + * @property {string} TM - string + * @property {string | null} TMPtr - string | null + * @property {any} CJM - any + * @property {any | null} CJMPtr - any | null + * @property {string} CTM - string + * @property {string | null} CTMPtr - string | null + * @property {any} CM - any + * @property {any | null} CMPtr - any | null + * @property {AliasNonMarshaler} ANM + * @property {AliasNonMarshaler | null} ANMPtr - AliasNonMarshaler | null + * @property {AliasJsonMarshaler} AJM + * @property {AliasJsonMarshaler | null} AJMPtr - AliasJsonMarshaler | null + * @property {AliasTextMarshaler} ATM + * @property {AliasTextMarshaler | null} ATMPtr - AliasTextMarshaler | null + * @property {AliasMarshaler} AM + * @property {AliasMarshaler | null} AMPtr - AliasMarshaler | null + * @property {ImplicitJsonMarshaler} ImJM + * @property {ImplicitJsonMarshaler | null} ImJMPtr - ImplicitJsonMarshaler | null + * @property {ImplicitTextMarshaler} ImTM + * @property {ImplicitTextMarshaler | null} ImTMPtr - ImplicitTextMarshaler | null + * @property {ImplicitMarshaler} ImM + * @property {ImplicitMarshaler | null} ImMPtr - ImplicitMarshaler | null + * @property {ImplicitNonJson} ImNJ + * @property {ImplicitNonJson | null} ImNJPtr - ImplicitNonJson | null + * @property {ImplicitNonText} ImNT + * @property {ImplicitNonText | null} ImNTPtr - ImplicitNonText | null + * @property {ImplicitNonMarshaler} ImNM + * @property {ImplicitNonMarshaler | null} ImNMPtr - ImplicitNonMarshaler | null + * @property {ImplicitJsonButText} ImJbT + * @property {ImplicitJsonButText | null} ImJbTPtr - ImplicitJsonButText | null + * @property {ImplicitTextButJson} ImTbJ + * @property {ImplicitTextButJson | null} ImTbJPtr - ImplicitTextButJson | null + */ + +/** + * any + * @typedef {any} ImplicitJsonButText + */ + +/** + * any + * @typedef {any} ImplicitJsonMarshaler + */ + +/** + * any + * @typedef {any} ImplicitMarshaler + */ + +/** + * string + * @typedef {string} ImplicitNonJson + */ + +/** + * class{ Marshaler, TextMarshaler } + * @typedef {Object} ImplicitNonMarshaler + * @property {json$0.Marshaler} Marshaler + * @property {encoding$0.TextMarshaler} TextMarshaler + */ + +/** + * any + * @typedef {any} ImplicitNonText + */ + +/** + * any + * @typedef {any} ImplicitTextButJson + */ + +/** + * string + * @typedef {string} ImplicitTextMarshaler + */ + +/** + * class {} + * @typedef { { + * } } NonMarshaler + */ + +/** + * any + * @typedef {any} PointerJsonMarshaler + */ + +/** + * any + * @typedef {any} PointerMarshaler + */ + +/** + * string + * @typedef {string} PointerTextMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingJsonMarshaler + */ + +/** + * any + * @typedef {any} UnderlyingMarshaler + */ + +/** + * string + * @typedef {string} UnderlyingTextMarshaler + */ + +/** + * any + * @typedef {any} ValueJsonMarshaler + */ + +/** + * any + * @typedef {any} ValueMarshaler + */ + +/** + * string + * @typedef {string} ValueTextMarshaler + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js new file mode 100644 index 000000000..9ad0c6d96 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.js @@ -0,0 +1,18 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @returns {$CancellablePromise<$models.Data>} + */ +export function Method() { + return $Call.ByName("main.Service.Method"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js new file mode 100644 index 000000000..0b7f42650 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +import * as $models from "./models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + * @template How + * @typedef {$models.HowDifferent} HowDifferent + */ + +/** + * Impersonator gets their fields from other people. + * @typedef {$models.Impersonator} Impersonator + */ + +/** + * Person is not a number. + * @typedef {$models.Person} Person + */ + +/** + * PrivatePerson gets their fields from hidden sources. + * @typedef {$models.PrivatePerson} PrivatePerson + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js new file mode 100644 index 000000000..36f231303 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.js @@ -0,0 +1,44 @@ +// @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 * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + * @template How + * @typedef {Object} HowDifferent + * @property {string} Name - They have a name as well. + * @property {({ [_: string]: How } | null)[] | null} Differences - But they may have many differences. + */ + +/** + * Impersonator gets their fields from other people. + * @typedef {other$0.OtherPerson} Impersonator + */ + +/** + * Person is not a number. + * @typedef {Object} Person + * @property {string} Name - They have a name. + * @property {Impersonator[]} Friends - Exactly 4 sketchy friends. + */ + +/** + * PrivatePerson gets their fields from hidden sources. + * @typedef {personImpl} PrivatePerson + */ + +/** + * @typedef {Object} personImpl + * @property {string} Nickname - Nickname conceals a person's identity. + * @property {string} Name - They have a name. + * @property {Impersonator[]} Friends - Exactly 4 sketchy friends. + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js new file mode 100644 index 000000000..33246d35e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.js @@ -0,0 +1,16 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +import * as $models from "./models.js"; + +/** + * OtherPerson is like a person, but different. + * @template T + * @typedef {$models.OtherPerson} OtherPerson + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js new file mode 100644 index 000000000..63a2ee722 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.js @@ -0,0 +1,15 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherPerson is like a person, but different. + * @template T + * @typedef {Object} OtherPerson + * @property {string} Name - They have a name as well. + * @property {T[] | null} Differences - But they may have many differences. + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js new file mode 100644 index 000000000..c3f8ff04b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other.OtherMethods.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js new file mode 100644 index 000000000..92435f679 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOne"); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js new file mode 100644 index 000000000..490c12c08 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.js @@ -0,0 +1,20 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("main.EmbedOther.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js new file mode 100644 index 000000000..5f940b2c7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.js @@ -0,0 +1,32 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + * @returns {$CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]>} + */ +export function LikeThisOne() { + return $Call.ByName("main.EmbedService.LikeThisOne"); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + * @returns {$CancellablePromise} + */ +export function LikeThisOtherOne() { + return $Call.ByName("main.EmbedService.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js new file mode 100644 index 000000000..f8c6b19b2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} $0 + * @returns {$CancellablePromise} + */ +export function Greet($0) { + return $Call.ByName("main.GreetService.Greet", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js new file mode 100644 index 000000000..734fb02e7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.js @@ -0,0 +1,12 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js new file mode 100644 index 000000000..e2ba84581 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js new file mode 100644 index 000000000..2fdca31cc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js new file mode 100644 index 000000000..e2ba84581 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.js @@ -0,0 +1,15 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js new file mode 100644 index 000000000..62ddbc166 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js new file mode 100644 index 000000000..2fdca31cc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.js @@ -0,0 +1,14 @@ +// @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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * @returns {$CancellablePromise} + */ +export function Hello() { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js new file mode 100644 index 000000000..8ab5f3462 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js new file mode 100644 index 000000000..41b452f57 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.js @@ -0,0 +1,17 @@ +// @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 * as services$0 from "./services/models.js"; + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {services$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js new file mode 100644 index 000000000..911d42560 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js new file mode 100644 index 000000000..eca08a018 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.js @@ -0,0 +1,360 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] | null } | null>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js new file mode 100644 index 000000000..ec6136e27 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {Person | null} Parent + * @property {{"Age": number, "Address": {"Street": string}}} Details + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js new file mode 100644 index 000000000..eca08a018 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.js @@ -0,0 +1,360 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {number[]} $in + * @returns {$CancellablePromise} + */ +export function ArrayInt($in) { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +/** + * @param {boolean} $in + * @returns {$CancellablePromise} + */ +export function BoolInBoolOut($in) { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float32InFloat32Out($in) { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Float64InFloat64Out($in) { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int16InIntOut($in) { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int32InIntOut($in) { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int64InIntOut($in) { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function Int8InIntOut($in) { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function Int8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function IntInIntOut($in) { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function IntPointerInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntInt($in) { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntIntPointer($in) { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise} + */ +export function MapIntSliceInt($in) { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +/** + * @param {{ [_: `${number}`]: number[] | null } | null} $in + * @returns {$CancellablePromise<{ [_: `${number}`]: number[] | null } | null>} + */ +export function MapIntSliceIntInMapIntSliceIntOut($in) { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in); +} + +/** + * @returns {$CancellablePromise} + */ +export function NoInputsStringOut() { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +/** + * @param {boolean | null} $in + * @returns {$CancellablePromise} + */ +export function PointerBoolInBoolOut($in) { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat32InFloat32Out($in) { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function PointerFloat64InFloat64Out($in) { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +/** + * @param {{ [_: `${number}`]: number } | null} $in + * @returns {$CancellablePromise} + */ +export function PointerMapIntInt($in) { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +/** + * @param {string | null} $in + * @returns {$CancellablePromise} + */ +export function PointerStringInStringOut($in) { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutput($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputNamedOutputs($in) { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringArrayOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in); +} + +/** + * @param {string[] | null} $in + * @returns {$CancellablePromise} + */ +export function StringArrayInputStringOut($in) { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +/** + * @param {$models.Person} $in + * @returns {$CancellablePromise<$models.Person>} + */ +export function StructInputStructOutput($in) { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise} + */ +export function StructPointerInputErrorOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +/** + * @param {$models.Person | null} $in + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function StructPointerInputStructPointerOutput($in) { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt16InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt16PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt32InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt32PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt64InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt64PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UInt8InUIntOut($in) { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UInt8PointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +/** + * @param {number} $in + * @returns {$CancellablePromise} + */ +export function UIntInUIntOut($in) { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +/** + * @param {number | null} $in + * @returns {$CancellablePromise} + */ +export function UIntPointerInAndOutput($in) { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js new file mode 100644 index 000000000..cb2979a7a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js new file mode 100644 index 000000000..ec6136e27 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Person + * @property {string} Name + * @property {Person | null} Parent + * @property {{"Age": number, "Address": {"Street": string}}} Details + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js new file mode 100644 index 000000000..bba7ea7ea --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js new file mode 100644 index 000000000..bba7ea7ea --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.js @@ -0,0 +1,21 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.js new file mode 100644 index 000000000..fdf1ff435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/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 GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js new file mode 100644 index 000000000..8ab5f3462 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.js @@ -0,0 +1,34 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * @param {string} name + * @returns {$CancellablePromise} + */ +export function Greet(name) { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + * @param {string} name + * @returns {$CancellablePromise<$models.Person | null>} + */ +export function NewPerson(name) { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js new file mode 100644 index 000000000..977600693 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.js @@ -0,0 +1,16 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +import * as $models from "./models.js"; + +/** + * Person is a person! + * They have a name and an address + * @typedef {$models.Person} Person + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js new file mode 100644 index 000000000..ab5cea255 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.js @@ -0,0 +1,19 @@ +// @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 * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + * @typedef {Object} Person + * @property {string} Name + * @property {services$0.Address | null} Address + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js new file mode 100644 index 000000000..588ef7ca7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +import * as $models from "./models.js"; + +/** + * @typedef {$models.Address} Address + */ diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js new file mode 100644 index 000000000..c04e3b10b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.js @@ -0,0 +1,14 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * @typedef {Object} Address + * @property {string} Street + * @property {string} State + * @property {string} Country + */ + +// In interface mode, this file is likely to contain just comments. +// We add a dummy export statement to ensure it is recognised as an ES module. +export {}; diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js new file mode 100644 index 000000000..6594edc5b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.js @@ -0,0 +1,25 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + * @returns {$CancellablePromise<$models.Address | null>} + */ +export function Yay() { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/warnings.log b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=JS/UseInterfaces=true/UseNames=true/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/index.ts new file mode 100644 index 000000000..ba2885969 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + TextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/json/index.ts new file mode 100644 index 000000000..00ec01151 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/json/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Marshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/json/models.ts new file mode 100644 index 000000000..8cd1a164f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/json/models.ts @@ -0,0 +1,12 @@ +// 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 "/wails/runtime.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + */ +export type Marshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/models.ts new file mode 100644 index 000000000..235dfce3e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/encoding/models.ts @@ -0,0 +1,14 @@ +// 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 "/wails/runtime.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + */ +export type TextMarshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts new file mode 100644 index 000000000..3c6865881 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts @@ -0,0 +1,82 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + */ +export function Get(aliasValue: $models.Alias): $CancellablePromise<$models.Person> { + return $Call.ByID(1928502664, aliasValue).then(($result: any) => { + return $$createType0($result); + }); +} + +/** + * Apparently, aliases are all the rage right now. + */ +export function GetButAliased(p: $models.AliasedPerson): $CancellablePromise<$models.StrangelyAliasedPerson> { + return $Call.ByID(1896499664, p).then(($result: any) => { + return $$createType0($result); + }); +} + +/** + * Get someone quite different. + */ +export function GetButDifferent(): $CancellablePromise<$models.GenericPerson> { + return $Call.ByID(2240931744).then(($result: any) => { + return $$createType1($result); + }); +} + +export function GetButForeignPrivateAlias(): $CancellablePromise { + return $Call.ByID(643456960).then(($result: any) => { + return $$createType2($result); + }); +} + +export function GetButGenericAliases(): $CancellablePromise<$models.AliasGroup> { + return $Call.ByID(914093800).then(($result: any) => { + return $$createType3($result); + }); +} + +/** + * Greet a lot of unusual things. + */ +export function Greet($0: $models.EmptyAliasStruct, $1: $models.EmptyStruct): $CancellablePromise<$models.AliasStruct> { + return $Call.ByID(1411160069, $0, $1).then(($result: any) => { + return $$createType7($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.GenericPerson.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; +const $$createType3 = $models.AliasGroup.createFrom; +const $$createType4 = $Create.Array($Create.Any); +const $$createType5 = $Create.Array($Create.Any); +const $$createType6 = $Create.Struct({ + "NoMoreIdeas": $$createType5, +}); +const $$createType7 = $Create.Struct({ + "Foo": $$createType4, + "Other": $$createType6, +}); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts new file mode 100644 index 000000000..13f61da0f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + AliasGroup, + AliasedPerson, + EmptyStruct, + GenericPerson, + GenericPersonAlias, + IndirectPersonAlias, + Person, + StrangelyAliasedPerson, + TPIndirectPersonAlias +} from "./models.js"; + +export type { + Alias, + AliasStruct, + EmptyAliasStruct, + GenericAlias, + GenericMapAlias, + GenericPtrAlias, + OtherAliasStruct +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts new file mode 100644 index 000000000..63ca43914 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts @@ -0,0 +1,290 @@ +// 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 "/wails/runtime.js"; + +/** + * A nice type Alias. + */ +export type Alias = number; + +/** + * A class whose fields have various aliased types. + */ +export class AliasGroup { + "GAi": GenericAlias; + "GAP": GenericAlias>; + "GPAs": GenericPtrAlias; + "GPAP": GenericPtrAlias>; + "GMA": GenericMapAlias; + "GPA": GenericPersonAlias; + "IPA": IndirectPersonAlias; + "TPIPA": TPIndirectPersonAlias; + + /** Creates a new AliasGroup instance. */ + constructor($$source: Partial = {}) { + if (!("GAi" in $$source)) { + this["GAi"] = 0; + } + if (!("GAP" in $$source)) { + this["GAP"] = (new GenericPerson()); + } + if (!("GPAs" in $$source)) { + this["GPAs"] = null; + } + if (!("GPAP" in $$source)) { + this["GPAP"] = null; + } + if (!("GMA" in $$source)) { + this["GMA"] = {}; + } + if (!("GPA" in $$source)) { + this["GPA"] = (new GenericPersonAlias()); + } + if (!("IPA" in $$source)) { + this["IPA"] = (new IndirectPersonAlias()); + } + if (!("TPIPA" in $$source)) { + this["TPIPA"] = (new TPIndirectPersonAlias()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new AliasGroup instance from a string or object. + */ + static createFrom($$source: any = {}): AliasGroup { + const $$createField1_0 = $$createType0; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType5; + const $$createField4_0 = $$createType6; + const $$createField5_0 = $$createType8; + const $$createField6_0 = $$createType8; + const $$createField7_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("GAP" in $$parsedSource) { + $$parsedSource["GAP"] = $$createField1_0($$parsedSource["GAP"]); + } + if ("GPAs" in $$parsedSource) { + $$parsedSource["GPAs"] = $$createField2_0($$parsedSource["GPAs"]); + } + if ("GPAP" in $$parsedSource) { + $$parsedSource["GPAP"] = $$createField3_0($$parsedSource["GPAP"]); + } + if ("GMA" in $$parsedSource) { + $$parsedSource["GMA"] = $$createField4_0($$parsedSource["GMA"]); + } + if ("GPA" in $$parsedSource) { + $$parsedSource["GPA"] = $$createField5_0($$parsedSource["GPA"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField6_0($$parsedSource["IPA"]); + } + if ("TPIPA" in $$parsedSource) { + $$parsedSource["TPIPA"] = $$createField7_0($$parsedSource["TPIPA"]); + } + return new AliasGroup($$parsedSource as Partial); + } +} + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + */ +export interface AliasStruct { + /** + * A field with a comment. + */ + "Foo": number[]; + + /** + * Definitely not Foo. + */ + "Bar"?: string; + "Baz"?: string; + + /** + * A nested alias struct. + */ + "Other": OtherAliasStruct; +} + +/** + * An empty struct alias. + */ +export interface EmptyAliasStruct { +} + +/** + * An empty struct. + */ +export class EmptyStruct { + + /** Creates a new EmptyStruct instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new EmptyStruct instance from a string or object. + */ + static createFrom($$source: any = {}): EmptyStruct { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new EmptyStruct($$parsedSource as Partial); + } +} + +/** + * A generic alias that forwards to a type parameter. + */ +export type GenericAlias = T; + +/** + * A generic alias that wraps a map. + */ +export type GenericMapAlias = { [_: string]: U }; + +/** + * A generic struct containing an alias. + */ +export class GenericPerson { + "Name"?: T; + "AliasedField": Alias; + + /** Creates a new GenericPerson instance. */ + constructor($$source: Partial> = {}) { + if (!("AliasedField" in $$source)) { + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class GenericPerson. + */ + static createFrom($$createParamT: (source: any) => T): ($$source?: any) => GenericPerson { + const $$createField0_0 = $$createParamT; + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Name" in $$parsedSource) { + $$parsedSource["Name"] = $$createField0_0($$parsedSource["Name"]); + } + return new GenericPerson($$parsedSource as Partial>); + }; + } +} + +/** + * A generic alias that wraps a generic struct. + */ +export const GenericPersonAlias = GenericPerson; + +/** + * A generic alias that wraps a generic struct. + */ +export type GenericPersonAlias = GenericPerson[]>; + +/** + * A generic alias that wraps a pointer type. + */ +export type GenericPtrAlias = GenericAlias | null; + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export const IndirectPersonAlias = GenericPersonAlias; + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export type IndirectPersonAlias = GenericPersonAlias; + +/** + * Another struct alias. + */ +export interface OtherAliasStruct { + "NoMoreIdeas": number[]; +} + +/** + * A non-generic struct containing an alias. + */ +export class Person { + /** + * The Person's name. + */ + "Name": string; + + /** + * A random alias field. + */ + "AliasedField": Alias; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("AliasedField" in $$source)) { + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person($$parsedSource as Partial); + } +} + +/** + * A class alias. + */ +export const AliasedPerson = Person; + +/** + * A class alias. + */ +export type AliasedPerson = Person; + +/** + * Another class alias, but ordered after its aliased class. + */ +export const StrangelyAliasedPerson = Person; + +/** + * Another class alias, but ordered after its aliased class. + */ +export type StrangelyAliasedPerson = Person; + +/** + * An alias that wraps a class through a typeparam alias. + */ +export const TPIndirectPersonAlias = GenericPerson; + +/** + * An alias that wraps a class through a typeparam alias. + */ +export type TPIndirectPersonAlias = GenericAlias>; + +// Private type creation functions +const $$createType0 = GenericPerson.createFrom($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = $Create.Nullable($$createType1); +const $$createType3 = $Create.Array($Create.Any); +const $$createType4 = GenericPerson.createFrom($$createType3); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Array($Create.Any); +const $$createType8 = GenericPerson.createFrom($$createType7); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts new file mode 100644 index 000000000..bdcf43c67 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts new file mode 100644 index 000000000..8fe0035ed --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function TestMethod(): $CancellablePromise { + return $Call.ByID(2241101727); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts new file mode 100644 index 000000000..8260ce500 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function TestMethod2(): $CancellablePromise { + return $Call.ByID(1556848345); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts new file mode 100644 index 000000000..69c89433a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(person: $models.Person, emb: $models.Embedded1): $CancellablePromise { + return $Call.ByID(1411160069, person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts new file mode 100644 index 000000000..6cdc52c66 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts @@ -0,0 +1,17 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Embedded1, + Person, + Title +} from "./models.js"; + +export type { + Embedded3 +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts new file mode 100644 index 000000000..50f23b52d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts @@ -0,0 +1,237 @@ +// 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 "/wails/runtime.js"; + +export class Embedded1 { + /** + * Friends should be shadowed in Person by a field of lesser depth + */ + "Friends": number; + + /** + * Vanish should be omitted from Person because there is another field with same depth and no tag + */ + "Vanish": number; + + /** + * StillThere should be shadowed in Person by other field with same depth and a json tag + */ + "StillThere": string; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** Creates a new Embedded1 instance. */ + constructor($$source: Partial = {}) { + if (!("Friends" in $$source)) { + this["Friends"] = 0; + } + if (!("Vanish" in $$source)) { + this["Vanish"] = 0; + } + if (!("StillThere" in $$source)) { + this["StillThere"] = ""; + } + if (!("NamingThingsIsHard" in $$source)) { + this["NamingThingsIsHard"] = "false"; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Embedded1 instance from a string or object. + */ + static createFrom($$source: any = {}): Embedded1 { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Embedded1($$parsedSource as Partial); + } +} + +export type Embedded3 = string; + +/** + * Person represents a person + */ +export class Person { + /** + * Titles is optional in JSON + */ + "Titles"?: Title[]; + + /** + * Names has a + * multiline comment + */ + "Names": string[]; + + /** + * Partner has a custom and complex JSON key + */ + "Partner": Person | null; + "Friends": (Person | null)[]; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * StillThereButRenamed should shadow in Person the other field with same depth and no json tag + */ + "StillThere": Embedded3 | null; + + /** + * StrangeNumber maps to "-" + */ + "-": number; + + /** + * Embedded3 should appear with key "Embedded3" + */ + "Embedded3": Embedded3; + + /** + * StrangerNumber is serialized as a string + */ + "StrangerNumber": `${number}`; + + /** + * StrangestString is optional and serialized as a JSON string + */ + "StrangestString"?: `"${string}"`; + + /** + * StringStrangest is serialized as a JSON string and optional + */ + "StringStrangest"?: `"${string}"`; + + /** + * embedded4 should be optional and appear with key "emb4" + */ + "emb4"?: embedded4; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Names" in $$source)) { + this["Names"] = []; + } + if (!("Partner" in $$source)) { + this["Partner"] = null; + } + if (!("Friends" in $$source)) { + this["Friends"] = []; + } + if (!("NamingThingsIsHard" in $$source)) { + this["NamingThingsIsHard"] = "false"; + } + if (!("StillThere" in $$source)) { + this["StillThere"] = null; + } + if (!("-" in $$source)) { + this["-"] = 0; + } + if (!("Embedded3" in $$source)) { + this["Embedded3"] = ""; + } + if (!("StrangerNumber" in $$source)) { + this["StrangerNumber"] = "0"; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType3; + const $$createField3_0 = $$createType4; + const $$createField11_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Titles" in $$parsedSource) { + $$parsedSource["Titles"] = $$createField0_0($$parsedSource["Titles"]); + } + if ("Names" in $$parsedSource) { + $$parsedSource["Names"] = $$createField1_0($$parsedSource["Names"]); + } + if ("Partner" in $$parsedSource) { + $$parsedSource["Partner"] = $$createField2_0($$parsedSource["Partner"]); + } + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField3_0($$parsedSource["Friends"]); + } + if ("emb4" in $$parsedSource) { + $$parsedSource["emb4"] = $$createField11_0($$parsedSource["emb4"]); + } + return new Person($$parsedSource as Partial); + } +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; + +export class embedded4 { + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * Friends should not be shadowed in Person as embedded4 is not embedded + * from encoding/json's point of view; + * however, it should be shadowed in Embedded1 + */ + "Friends": boolean; + + /** Creates a new embedded4 instance. */ + constructor($$source: Partial = {}) { + if (!("NamingThingsIsHard" in $$source)) { + this["NamingThingsIsHard"] = "false"; + } + if (!("Friends" in $$source)) { + this["Friends"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new embedded4 instance from a string or object. + */ + static createFrom($$source: any = {}): embedded4 { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new embedded4($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = Person.createFrom; +const $$createType3 = $Create.Nullable($$createType2); +const $$createType4 = $Create.Array($$createType3); +const $$createType5 = embedded4.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts new file mode 100644 index 000000000..cc1e88ad7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts @@ -0,0 +1,32 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + */ +export function Greet(str: string, people: $models.Person[], $2: {"AnotherCount": number, "AnotherOne": $models.Person | null}, assoc: { [_: `${number}`]: boolean | null }, $4: (number | null)[], ...other: string[]): $CancellablePromise<[$models.Person, any, number[]]> { + return $Call.ByID(1411160069, str, people, $2, assoc, $4, other).then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[2] = $$createType1($result[2]); + return $result; + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Array($Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts new file mode 100644 index 000000000..2417aff4c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts @@ -0,0 +1,30 @@ +// 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 "/wails/runtime.js"; + +/** + * Person represents a person + */ +export class Person { + "Name": string; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person($$parsedSource as Partial); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts new file mode 100644 index 000000000..c1c70be69 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts @@ -0,0 +1,30 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.StructA, $models.StructC]> { + return $Call.ByID(440020721).then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + return $result; + }); +} + +// Private type creation functions +const $$createType0 = $models.StructA.createFrom; +const $$createType1 = $models.StructC.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts new file mode 100644 index 000000000..4b190b8da --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + StructA, + StructC, + StructE +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts new file mode 100644 index 000000000..9e86cd674 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts @@ -0,0 +1,131 @@ +// 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 "/wails/runtime.js"; + +export class StructA { + "B": structB | null; + + /** Creates a new StructA instance. */ + constructor($$source: Partial = {}) { + if (!("B" in $$source)) { + this["B"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructA instance from a string or object. + */ + static createFrom($$source: any = {}): StructA { + const $$createField0_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("B" in $$parsedSource) { + $$parsedSource["B"] = $$createField0_0($$parsedSource["B"]); + } + return new StructA($$parsedSource as Partial); + } +} + +export class StructC { + "D": structD; + + /** Creates a new StructC instance. */ + constructor($$source: Partial = {}) { + if (!("D" in $$source)) { + this["D"] = (new structD()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructC instance from a string or object. + */ + static createFrom($$source: any = {}): StructC { + const $$createField0_0 = $$createType2; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("D" in $$parsedSource) { + $$parsedSource["D"] = $$createField0_0($$parsedSource["D"]); + } + return new StructC($$parsedSource as Partial); + } +} + +export class StructE { + + /** Creates a new StructE instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new StructE instance from a string or object. + */ + static createFrom($$source: any = {}): StructE { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new StructE($$parsedSource as Partial); + } +} + +export class structB { + "A": StructA | null; + + /** Creates a new structB instance. */ + constructor($$source: Partial = {}) { + if (!("A" in $$source)) { + this["A"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structB instance from a string or object. + */ + static createFrom($$source: any = {}): structB { + const $$createField0_0 = $$createType4; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField0_0($$parsedSource["A"]); + } + return new structB($$parsedSource as Partial); + } +} + +export class structD { + "E": StructE; + + /** Creates a new structD instance. */ + constructor($$source: Partial = {}) { + if (!("E" in $$source)) { + this["E"] = (new StructE()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structD instance from a string or object. + */ + static createFrom($$source: any = {}): structD { + const $$createField0_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("E" in $$parsedSource) { + $$parsedSource["E"] = $$createField0_0($$parsedSource["E"]); + } + return new structD($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = structB.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = structD.createFrom; +const $$createType3 = StructA.createFrom; +const $$createType4 = $Create.Nullable($$createType3); +const $$createType5 = StructE.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts new file mode 100644 index 000000000..cbddfb346 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts @@ -0,0 +1,63 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]> { + return $Call.ByID(440020721).then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType9($result[1]); + return $result; + }); +} + +// Private type creation functions +var $$createType0 = (function $$initCreateType0(...args: any[]): any { + if ($$createType0 === $$initCreateType0) { + $$createType0 = $$createType3; + } + return $$createType0(...args); +}); +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = $Create.Map($Create.Any, $$createType1); +const $$createType3 = $Create.Array($$createType2); +var $$createType4 = (function $$initCreateType4(...args: any[]): any { + if ($$createType4 === $$initCreateType4) { + $$createType4 = $$createType8; + } + return $$createType4(...args); +}); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Array($Create.Any); +const $$createType7 = $Create.Struct({ + "X": $$createType5, + "Y": $$createType6, +}); +const $$createType8 = $Create.Array($$createType7); +var $$createType9 = (function $$initCreateType9(...args: any[]): any { + if ($$createType9 === $$initCreateType9) { + $$createType9 = $$createType13; + } + return $$createType9(...args); +}); +const $$createType10 = $Create.Nullable($$createType9); +const $$createType11 = $Create.Array($$createType4); +const $$createType12 = $Create.Struct({ + "X": $$createType10, + "Y": $$createType11, +}); +const $$createType13 = $Create.Array($$createType12); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts new file mode 100644 index 000000000..16cef660c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Alias, + Cyclic, + GenericCyclic +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts new file mode 100644 index 000000000..93d1ef5c6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts @@ -0,0 +1,12 @@ +// 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 "/wails/runtime.js"; + +export type Alias = Cyclic | null; + +export type Cyclic = { [_: string]: Alias }[]; + +export type GenericCyclic = {"X": GenericCyclic | null, "Y": T[]}[]; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts new file mode 100644 index 000000000..b9fbdba96 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Classes!"); +console.log("Hello TS!"); +console.log("Hello TS Classes!"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts new file mode 100644 index 000000000..b968330db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.InternalModel): $CancellablePromise { + return $Call.ByID(538079117, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts new file mode 100644 index 000000000..4d242fc2c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts @@ -0,0 +1,54 @@ +// 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 "/wails/runtime.js"; + +/** + * An exported but internal model. + */ +export class InternalModel { + "Field": string; + + /** Creates a new InternalModel instance. */ + constructor($$source: Partial = {}) { + if (!("Field" in $$source)) { + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new InternalModel instance from a string or object. + */ + static createFrom($$source: any = {}): InternalModel { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new InternalModel($$parsedSource as Partial); + } +} + +/** + * An unexported model. + */ +export class unexportedModel { + "Field": string; + + /** Creates a new unexportedModel instance. */ + constructor($$source: Partial = {}) { + if (!("Field" in $$source)) { + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new unexportedModel instance from a string or object. + */ + static createFrom($$source: any = {}): unexportedModel { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new unexportedModel($$parsedSource as Partial); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts new file mode 100644 index 000000000..e52a0ccb8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Dummy +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts new file mode 100644 index 000000000..b927155d5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts @@ -0,0 +1,23 @@ +// 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 "/wails/runtime.js"; + +export class Dummy { + + /** Creates a new Dummy instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new Dummy instance from a string or object. + */ + static createFrom($$source: any = {}): Dummy { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Dummy($$parsedSource as Partial); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_t.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_t.ts new file mode 100644 index 000000000..6703820f1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_tc.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_tc.ts new file mode 100644 index 000000000..15d2994e9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_tc.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("TS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts new file mode 100644 index 000000000..338c7fbd1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts @@ -0,0 +1,22 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +function InternalMethod($0: string): $CancellablePromise { + return $Call.ByID(3518775569, $0); +} + +export function VisibleMethod($0: otherpackage$0.Dummy): $CancellablePromise { + return $Call.ByID(474018228, $0); +} + +export async function CustomMethod(arg: string): Promise { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js new file mode 100644 index 000000000..724e79e12 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Classes"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts new file mode 100644 index 000000000..253d3f2f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_tc.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_tc.ts new file mode 100644 index 000000000..66b739d3a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_tc.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts new file mode 100644 index 000000000..a819daffd --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.unexportedModel): $CancellablePromise { + return $Call.ByID(37626172, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts new file mode 100644 index 000000000..b5d189f76 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts @@ -0,0 +1,47 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Comment 1. + */ +export function Method1(): $CancellablePromise { + return $Call.ByID(841558284); +} + +/** + * Comment 2. + */ +export function Method2(): $CancellablePromise { + return $Call.ByID(891891141); +} + +/** + * Comment 3a. + * Comment 3b. + */ +export function Method3(): $CancellablePromise { + return $Call.ByID(875113522); +} + +/** + * Comment 4. + */ +export function Method4(): $CancellablePromise { + return $Call.ByID(791225427); +} + +/** + * Comment 5. + */ +export function Method5(): $CancellablePromise { + return $Call.ByID(774447808); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts new file mode 100644 index 000000000..10de8838c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: $models.Title): $CancellablePromise { + return $Call.ByID(1411160069, name, title); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts new file mode 100644 index 000000000..3b4d2f5c6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Person, + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts new file mode 100644 index 000000000..a50282a38 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts @@ -0,0 +1,82 @@ +// 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 "/wails/runtime.js"; + +/** + * Age is an integer with some predefined values + */ +export type Age = number; + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + */ +export class Person { + "Title": Title; + "Name": string; + "Age": Age; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Title" in $$source)) { + this["Title"] = Title.$zero; + } + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Age" in $$source)) { + this["Age"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person($$parsedSource as Partial); + } +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts new file mode 100644 index 000000000..3bce7b000 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: services$0.Title): $CancellablePromise { + return $Call.ByID(1411160069, name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts new file mode 100644 index 000000000..01c612edc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts new file mode 100644 index 000000000..661222bdf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts @@ -0,0 +1,22 @@ +// 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 "/wails/runtime.js"; + +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts new file mode 100644 index 000000000..88cafa9d1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts new file mode 100644 index 000000000..f1ff262a1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts @@ -0,0 +1,46 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person + */ +export class Person { + "Name": string; + "Address": services$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts new file mode 100644 index 000000000..8dc381545 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(2007737399).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts new file mode 100644 index 000000000..88cafa9d1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts new file mode 100644 index 000000000..88b2c78db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./services/other/models.js"; + +export class Person { + "Name": string; + "Address": other$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = other$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts new file mode 100644 index 000000000..fe69a1da0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(2447353446).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts new file mode 100644 index 000000000..62283209b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts new file mode 100644 index 000000000..6e6ac2007 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts new file mode 100644 index 000000000..386913d65 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts @@ -0,0 +1,25 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * Greet someone + */ +export function GreetWithContext(name: string): $CancellablePromise { + return $Call.ByID(1310150960, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts new file mode 100644 index 000000000..6e6ac2007 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts new file mode 100644 index 000000000..b5890af28 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts @@ -0,0 +1,33 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Maps +} from "./models.js"; + +export type { + BasicCstrAlias, + ComparableCstrAlias, + EmbeddedCustomInterface, + EmbeddedOriginalInterface, + EmbeddedPointer, + EmbeddedPointerPtr, + EmbeddedValue, + EmbeddedValuePtr, + GoodTildeCstrAlias, + InterfaceCstrAlias, + MixedCstrAlias, + NonBasicCstrAlias, + PointableCstrAlias, + PointerAlias, + PointerTextMarshaler, + StringAlias, + StringType, + ValueAlias, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts new file mode 100644 index 000000000..0d26a6b0b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts @@ -0,0 +1,1886 @@ +// 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 "/wails/runtime.js"; + +export type BasicCstrAlias = S; + +export type ComparableCstrAlias = R; + +export type EmbeddedCustomInterface = string; + +export type EmbeddedOriginalInterface = string; + +export type EmbeddedPointer = string; + +export type EmbeddedPointerPtr = string; + +export type EmbeddedValue = string; + +export type EmbeddedValuePtr = string; + +export type GoodTildeCstrAlias = U; + +export type InterfaceCstrAlias = Y; + +export class Maps { + /** + * Reject + */ + "Bool": { [_: string]: number }; + + /** + * Accept + */ + "Int": { [_: `${number}`]: number }; + + /** + * Accept + */ + "Uint": { [_: `${number}`]: number }; + + /** + * Reject + */ + "Float": { [_: string]: number }; + + /** + * Reject + */ + "Complex": { [_: string]: number }; + + /** + * Accept + */ + "Byte": { [_: `${number}`]: number }; + + /** + * Accept + */ + "Rune": { [_: `${number}`]: number }; + + /** + * Accept + */ + "String": { [_: string]: number }; + + /** + * Reject + */ + "IntPtr": { [_: string]: number }; + + /** + * Reject + */ + "UintPtr": { [_: string]: number }; + + /** + * Reject + */ + "FloatPtr": { [_: string]: number }; + + /** + * Reject + */ + "ComplexPtr": { [_: string]: number }; + + /** + * Reject + */ + "StringPtr": { [_: string]: number }; + + /** + * Reject + */ + "NTM": { [_: string]: number }; + + /** + * Reject + */ + "NTMPtr": { [_: string]: number }; + + /** + * Accept + */ + "VTM": { [_: ValueTextMarshaler]: number }; + + /** + * Accept + */ + "VTMPtr": { [_: ValueTextMarshaler]: number }; + + /** + * Reject + */ + "PTM": { [_: string]: number }; + + /** + * Accept + */ + "PTMPtr": { [_: PointerTextMarshaler]: number }; + + /** + * Accept, hide + */ + "JTM": { [_: string]: number }; + + /** + * Accept, hide + */ + "JTMPtr": { [_: string]: number }; + + /** + * Reject + */ + "A": { [_: string]: number }; + + /** + * Reject + */ + "APtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TM": { [_: string]: number }; + + /** + * Reject + */ + "TMPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "CI": { [_: string]: number }; + + /** + * Reject + */ + "CIPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "EI": { [_: string]: number }; + + /** + * Reject + */ + "EIPtr": { [_: string]: number }; + + /** + * Accept + */ + "EV": { [_: EmbeddedValue]: number }; + + /** + * Accept + */ + "EVPtr": { [_: EmbeddedValue]: number }; + + /** + * Accept + */ + "EVP": { [_: EmbeddedValuePtr]: number }; + + /** + * Accept + */ + "EVPPtr": { [_: EmbeddedValuePtr]: number }; + + /** + * Reject + */ + "EP": { [_: string]: number }; + + /** + * Accept + */ + "EPPtr": { [_: EmbeddedPointer]: number }; + + /** + * Accept + */ + "EPP": { [_: EmbeddedPointerPtr]: number }; + + /** + * Accept + */ + "EPPPtr": { [_: EmbeddedPointerPtr]: number }; + + /** + * Accept + */ + "ECI": { [_: EmbeddedCustomInterface]: number }; + + /** + * Accept + */ + "ECIPtr": { [_: EmbeddedCustomInterface]: number }; + + /** + * Accept + */ + "EOI": { [_: EmbeddedOriginalInterface]: number }; + + /** + * Accept + */ + "EOIPtr": { [_: EmbeddedOriginalInterface]: number }; + + /** + * Reject + */ + "WT": { [_: string]: number }; + + /** + * Reject + */ + "WA": { [_: string]: number }; + + /** + * Accept + */ + "ST": { [_: StringType]: number }; + + /** + * Accept + */ + "SA": { [_: StringAlias]: number }; + + /** + * Accept + */ + "IntT": { [_: `${number}`]: number }; + + /** + * Accept + */ + "IntA": { [_: `${number}`]: number }; + + /** + * Reject + */ + "VT": { [_: string]: number }; + + /** + * Reject + */ + "VTPtr": { [_: string]: number }; + + /** + * Reject + */ + "VPT": { [_: string]: number }; + + /** + * Reject + */ + "VPTPtr": { [_: string]: number }; + + /** + * Accept + */ + "VA": { [_: ValueAlias]: number }; + + /** + * Accept + */ + "VAPtr": { [_: ValueAlias]: number }; + + /** + * Accept, hide + */ + "VPA": { [_: string]: number }; + + /** + * Reject + */ + "VPAPtr": { [_: string]: number }; + + /** + * Reject + */ + "PT": { [_: string]: number }; + + /** + * Reject + */ + "PTPtr": { [_: string]: number }; + + /** + * Reject + */ + "PPT": { [_: string]: number }; + + /** + * Reject + */ + "PPTPtr": { [_: string]: number }; + + /** + * Reject + */ + "PA": { [_: string]: number }; + + /** + * Accept + */ + "PAPtr": { [_: PointerAlias]: number }; + + /** + * Accept, hide + */ + "PPA": { [_: string]: number }; + + /** + * Reject + */ + "PPAPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "IT": { [_: string]: number }; + + /** + * Reject + */ + "ITPtr": { [_: string]: number }; + + /** + * Reject + */ + "IPT": { [_: string]: number }; + + /** + * Reject + */ + "IPTPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "IA": { [_: string]: number }; + + /** + * Reject + */ + "IAPtr": { [_: string]: number }; + + /** + * Reject + */ + "IPA": { [_: string]: number }; + + /** + * Reject + */ + "IPAPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "TPR": { [_: string]: number }; + + /** + * Soft reject + */ + "TPRPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPS": { [_: string]: number }; + + /** + * Soft reject + */ + "TPSPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "TPT": { [_: string]: number }; + + /** + * Soft reject + */ + "TPTPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPU": { [_: string]: number }; + + /** + * Soft reject + */ + "TPUPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPV": { [_: string]: number }; + + /** + * Soft reject + */ + "TPVPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "TPW": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPWPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPX": { [_: string]: number }; + + /** + * Soft reject + */ + "TPXPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPY": { [_: string]: number }; + + /** + * Soft reject + */ + "TPYPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPZ": { [_: string]: number }; + + /** + * Soft reject + */ + "TPZPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "GAR": { [_: string]: number }; + + /** + * Soft reject + */ + "GARPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAS": { [_: string]: number }; + + /** + * Soft reject + */ + "GASPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "GAT": { [_: string]: number }; + + /** + * Soft reject + */ + "GATPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAU": { [_: string]: number }; + + /** + * Soft reject + */ + "GAUPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAV": { [_: string]: number }; + + /** + * Soft reject + */ + "GAVPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "GAW": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAWPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAX": { [_: string]: number }; + + /** + * Soft reject + */ + "GAXPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAY": { [_: string]: number }; + + /** + * Soft reject + */ + "GAYPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAZ": { [_: string]: number }; + + /** + * Soft reject + */ + "GAZPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GACi": { [_: `${number}`]: number }; + + /** + * Accept + */ + "GACV": { [_: ComparableCstrAlias]: number }; + + /** + * Reject + */ + "GACP": { [_: string]: number }; + + /** + * Reject + */ + "GACiPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GACVPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GACPPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GABi": { [_: `${number}`]: number }; + + /** + * Accept + */ + "GABs": { [_: BasicCstrAlias]: number }; + + /** + * Reject + */ + "GABiPtr": { [_: string]: number }; + + /** + * Reject + */ + "GABT": { [_: string]: number }; + + /** + * Reject + */ + "GABTPtr": { [_: string]: number }; + + /** + * Accept + */ + "GAGT": { [_: GoodTildeCstrAlias]: number }; + + /** + * Accept, hide + */ + "GAGTPtr": { [_: string]: number }; + + /** + * Accept + */ + "GANBV": { [_: NonBasicCstrAlias]: number }; + + /** + * Accept, hide + */ + "GANBP": { [_: string]: number }; + + /** + * Accept, hide + */ + "GANBVPtr": { [_: string]: number }; + + /** + * Reject + */ + "GANBPPtr": { [_: string]: number }; + + /** + * Accept + */ + "GAPlV1": { [_: PointableCstrAlias]: number }; + + /** + * Accept + */ + "GAPlV2": { [_: PointableCstrAlias]: number }; + + /** + * Reject + */ + "GAPlP1": { [_: string]: number }; + + /** + * Accept + */ + "GAPlP2": { [_: PointableCstrAlias]: number }; + + /** + * Accept, hide + */ + "GAPlVPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAPlPPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAMi": { [_: `${number}`]: number }; + + /** + * Accept + */ + "GAMS": { [_: MixedCstrAlias]: number }; + + /** + * Accept + */ + "GAMV": { [_: MixedCstrAlias]: number }; + + /** + * Reject + */ + "GAMSPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAMVPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAII": { [_: string]: number }; + + /** + * Accept + */ + "GAIV": { [_: InterfaceCstrAlias]: number }; + + /** + * Accept, hide + */ + "GAIP": { [_: string]: number }; + + /** + * Reject + */ + "GAIIPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAIVPtr": { [_: string]: number }; + + /** + * Reject + */ + "GAIPPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAPrV": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAPrP": { [_: string]: number }; + + /** + * Reject + */ + "GAPrVPtr": { [_: string]: number }; + + /** + * Reject + */ + "GAPrPPtr": { [_: string]: number }; + + /** Creates a new Maps instance. */ + constructor($$source: Partial> = {}) { + if (!("Bool" in $$source)) { + this["Bool"] = {}; + } + if (!("Int" in $$source)) { + this["Int"] = {}; + } + if (!("Uint" in $$source)) { + this["Uint"] = {}; + } + if (!("Float" in $$source)) { + this["Float"] = {}; + } + if (!("Complex" in $$source)) { + this["Complex"] = {}; + } + if (!("Byte" in $$source)) { + this["Byte"] = {}; + } + if (!("Rune" in $$source)) { + this["Rune"] = {}; + } + if (!("String" in $$source)) { + this["String"] = {}; + } + if (!("IntPtr" in $$source)) { + this["IntPtr"] = {}; + } + if (!("UintPtr" in $$source)) { + this["UintPtr"] = {}; + } + if (!("FloatPtr" in $$source)) { + this["FloatPtr"] = {}; + } + if (!("ComplexPtr" in $$source)) { + this["ComplexPtr"] = {}; + } + if (!("StringPtr" in $$source)) { + this["StringPtr"] = {}; + } + if (!("NTM" in $$source)) { + this["NTM"] = {}; + } + if (!("NTMPtr" in $$source)) { + this["NTMPtr"] = {}; + } + if (!("VTM" in $$source)) { + this["VTM"] = {}; + } + if (!("VTMPtr" in $$source)) { + this["VTMPtr"] = {}; + } + if (!("PTM" in $$source)) { + this["PTM"] = {}; + } + if (!("PTMPtr" in $$source)) { + this["PTMPtr"] = {}; + } + if (!("JTM" in $$source)) { + this["JTM"] = {}; + } + if (!("JTMPtr" in $$source)) { + this["JTMPtr"] = {}; + } + if (!("A" in $$source)) { + this["A"] = {}; + } + if (!("APtr" in $$source)) { + this["APtr"] = {}; + } + if (!("TM" in $$source)) { + this["TM"] = {}; + } + if (!("TMPtr" in $$source)) { + this["TMPtr"] = {}; + } + if (!("CI" in $$source)) { + this["CI"] = {}; + } + if (!("CIPtr" in $$source)) { + this["CIPtr"] = {}; + } + if (!("EI" in $$source)) { + this["EI"] = {}; + } + if (!("EIPtr" in $$source)) { + this["EIPtr"] = {}; + } + if (!("EV" in $$source)) { + this["EV"] = {}; + } + if (!("EVPtr" in $$source)) { + this["EVPtr"] = {}; + } + if (!("EVP" in $$source)) { + this["EVP"] = {}; + } + if (!("EVPPtr" in $$source)) { + this["EVPPtr"] = {}; + } + if (!("EP" in $$source)) { + this["EP"] = {}; + } + if (!("EPPtr" in $$source)) { + this["EPPtr"] = {}; + } + if (!("EPP" in $$source)) { + this["EPP"] = {}; + } + if (!("EPPPtr" in $$source)) { + this["EPPPtr"] = {}; + } + if (!("ECI" in $$source)) { + this["ECI"] = {}; + } + if (!("ECIPtr" in $$source)) { + this["ECIPtr"] = {}; + } + if (!("EOI" in $$source)) { + this["EOI"] = {}; + } + if (!("EOIPtr" in $$source)) { + this["EOIPtr"] = {}; + } + if (!("WT" in $$source)) { + this["WT"] = {}; + } + if (!("WA" in $$source)) { + this["WA"] = {}; + } + if (!("ST" in $$source)) { + this["ST"] = {}; + } + if (!("SA" in $$source)) { + this["SA"] = {}; + } + if (!("IntT" in $$source)) { + this["IntT"] = {}; + } + if (!("IntA" in $$source)) { + this["IntA"] = {}; + } + if (!("VT" in $$source)) { + this["VT"] = {}; + } + if (!("VTPtr" in $$source)) { + this["VTPtr"] = {}; + } + if (!("VPT" in $$source)) { + this["VPT"] = {}; + } + if (!("VPTPtr" in $$source)) { + this["VPTPtr"] = {}; + } + if (!("VA" in $$source)) { + this["VA"] = {}; + } + if (!("VAPtr" in $$source)) { + this["VAPtr"] = {}; + } + if (!("VPA" in $$source)) { + this["VPA"] = {}; + } + if (!("VPAPtr" in $$source)) { + this["VPAPtr"] = {}; + } + if (!("PT" in $$source)) { + this["PT"] = {}; + } + if (!("PTPtr" in $$source)) { + this["PTPtr"] = {}; + } + if (!("PPT" in $$source)) { + this["PPT"] = {}; + } + if (!("PPTPtr" in $$source)) { + this["PPTPtr"] = {}; + } + if (!("PA" in $$source)) { + this["PA"] = {}; + } + if (!("PAPtr" in $$source)) { + this["PAPtr"] = {}; + } + if (!("PPA" in $$source)) { + this["PPA"] = {}; + } + if (!("PPAPtr" in $$source)) { + this["PPAPtr"] = {}; + } + if (!("IT" in $$source)) { + this["IT"] = {}; + } + if (!("ITPtr" in $$source)) { + this["ITPtr"] = {}; + } + if (!("IPT" in $$source)) { + this["IPT"] = {}; + } + if (!("IPTPtr" in $$source)) { + this["IPTPtr"] = {}; + } + if (!("IA" in $$source)) { + this["IA"] = {}; + } + if (!("IAPtr" in $$source)) { + this["IAPtr"] = {}; + } + if (!("IPA" in $$source)) { + this["IPA"] = {}; + } + if (!("IPAPtr" in $$source)) { + this["IPAPtr"] = {}; + } + if (!("TPR" in $$source)) { + this["TPR"] = {}; + } + if (!("TPRPtr" in $$source)) { + this["TPRPtr"] = {}; + } + if (!("TPS" in $$source)) { + this["TPS"] = {}; + } + if (!("TPSPtr" in $$source)) { + this["TPSPtr"] = {}; + } + if (!("TPT" in $$source)) { + this["TPT"] = {}; + } + if (!("TPTPtr" in $$source)) { + this["TPTPtr"] = {}; + } + if (!("TPU" in $$source)) { + this["TPU"] = {}; + } + if (!("TPUPtr" in $$source)) { + this["TPUPtr"] = {}; + } + if (!("TPV" in $$source)) { + this["TPV"] = {}; + } + if (!("TPVPtr" in $$source)) { + this["TPVPtr"] = {}; + } + if (!("TPW" in $$source)) { + this["TPW"] = {}; + } + if (!("TPWPtr" in $$source)) { + this["TPWPtr"] = {}; + } + if (!("TPX" in $$source)) { + this["TPX"] = {}; + } + if (!("TPXPtr" in $$source)) { + this["TPXPtr"] = {}; + } + if (!("TPY" in $$source)) { + this["TPY"] = {}; + } + if (!("TPYPtr" in $$source)) { + this["TPYPtr"] = {}; + } + if (!("TPZ" in $$source)) { + this["TPZ"] = {}; + } + if (!("TPZPtr" in $$source)) { + this["TPZPtr"] = {}; + } + if (!("GAR" in $$source)) { + this["GAR"] = {}; + } + if (!("GARPtr" in $$source)) { + this["GARPtr"] = {}; + } + if (!("GAS" in $$source)) { + this["GAS"] = {}; + } + if (!("GASPtr" in $$source)) { + this["GASPtr"] = {}; + } + if (!("GAT" in $$source)) { + this["GAT"] = {}; + } + if (!("GATPtr" in $$source)) { + this["GATPtr"] = {}; + } + if (!("GAU" in $$source)) { + this["GAU"] = {}; + } + if (!("GAUPtr" in $$source)) { + this["GAUPtr"] = {}; + } + if (!("GAV" in $$source)) { + this["GAV"] = {}; + } + if (!("GAVPtr" in $$source)) { + this["GAVPtr"] = {}; + } + if (!("GAW" in $$source)) { + this["GAW"] = {}; + } + if (!("GAWPtr" in $$source)) { + this["GAWPtr"] = {}; + } + if (!("GAX" in $$source)) { + this["GAX"] = {}; + } + if (!("GAXPtr" in $$source)) { + this["GAXPtr"] = {}; + } + if (!("GAY" in $$source)) { + this["GAY"] = {}; + } + if (!("GAYPtr" in $$source)) { + this["GAYPtr"] = {}; + } + if (!("GAZ" in $$source)) { + this["GAZ"] = {}; + } + if (!("GAZPtr" in $$source)) { + this["GAZPtr"] = {}; + } + if (!("GACi" in $$source)) { + this["GACi"] = {}; + } + if (!("GACV" in $$source)) { + this["GACV"] = {}; + } + if (!("GACP" in $$source)) { + this["GACP"] = {}; + } + if (!("GACiPtr" in $$source)) { + this["GACiPtr"] = {}; + } + if (!("GACVPtr" in $$source)) { + this["GACVPtr"] = {}; + } + if (!("GACPPtr" in $$source)) { + this["GACPPtr"] = {}; + } + if (!("GABi" in $$source)) { + this["GABi"] = {}; + } + if (!("GABs" in $$source)) { + this["GABs"] = {}; + } + if (!("GABiPtr" in $$source)) { + this["GABiPtr"] = {}; + } + if (!("GABT" in $$source)) { + this["GABT"] = {}; + } + if (!("GABTPtr" in $$source)) { + this["GABTPtr"] = {}; + } + if (!("GAGT" in $$source)) { + this["GAGT"] = {}; + } + if (!("GAGTPtr" in $$source)) { + this["GAGTPtr"] = {}; + } + if (!("GANBV" in $$source)) { + this["GANBV"] = {}; + } + if (!("GANBP" in $$source)) { + this["GANBP"] = {}; + } + if (!("GANBVPtr" in $$source)) { + this["GANBVPtr"] = {}; + } + if (!("GANBPPtr" in $$source)) { + this["GANBPPtr"] = {}; + } + if (!("GAPlV1" in $$source)) { + this["GAPlV1"] = {}; + } + if (!("GAPlV2" in $$source)) { + this["GAPlV2"] = {}; + } + if (!("GAPlP1" in $$source)) { + this["GAPlP1"] = {}; + } + if (!("GAPlP2" in $$source)) { + this["GAPlP2"] = {}; + } + if (!("GAPlVPtr" in $$source)) { + this["GAPlVPtr"] = {}; + } + if (!("GAPlPPtr" in $$source)) { + this["GAPlPPtr"] = {}; + } + if (!("GAMi" in $$source)) { + this["GAMi"] = {}; + } + if (!("GAMS" in $$source)) { + this["GAMS"] = {}; + } + if (!("GAMV" in $$source)) { + this["GAMV"] = {}; + } + if (!("GAMSPtr" in $$source)) { + this["GAMSPtr"] = {}; + } + if (!("GAMVPtr" in $$source)) { + this["GAMVPtr"] = {}; + } + if (!("GAII" in $$source)) { + this["GAII"] = {}; + } + if (!("GAIV" in $$source)) { + this["GAIV"] = {}; + } + if (!("GAIP" in $$source)) { + this["GAIP"] = {}; + } + if (!("GAIIPtr" in $$source)) { + this["GAIIPtr"] = {}; + } + if (!("GAIVPtr" in $$source)) { + this["GAIVPtr"] = {}; + } + if (!("GAIPPtr" in $$source)) { + this["GAIPPtr"] = {}; + } + if (!("GAPrV" in $$source)) { + this["GAPrV"] = {}; + } + if (!("GAPrP" in $$source)) { + this["GAPrP"] = {}; + } + if (!("GAPrVPtr" in $$source)) { + this["GAPrVPtr"] = {}; + } + if (!("GAPrPPtr" in $$source)) { + this["GAPrPPtr"] = {}; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class Maps. + */ + static createFrom($$createParamR: (source: any) => R, $$createParamS: (source: any) => S, $$createParamT: (source: any) => T, $$createParamU: (source: any) => U, $$createParamV: (source: any) => V, $$createParamW: (source: any) => W, $$createParamX: (source: any) => X, $$createParamY: (source: any) => Y, $$createParamZ: (source: any) => Z): ($$source?: any) => Maps { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType3; + const $$createField4_0 = $$createType4; + const $$createField5_0 = $$createType5; + const $$createField6_0 = $$createType6; + const $$createField7_0 = $$createType7; + const $$createField8_0 = $$createType8; + const $$createField9_0 = $$createType9; + const $$createField10_0 = $$createType10; + const $$createField11_0 = $$createType11; + const $$createField12_0 = $$createType12; + const $$createField13_0 = $$createType13; + const $$createField14_0 = $$createType14; + const $$createField15_0 = $$createType15; + const $$createField16_0 = $$createType16; + const $$createField17_0 = $$createType17; + const $$createField18_0 = $$createType18; + const $$createField19_0 = $$createType19; + const $$createField20_0 = $$createType20; + const $$createField21_0 = $$createType21; + const $$createField22_0 = $$createType22; + const $$createField23_0 = $$createType23; + const $$createField24_0 = $$createType24; + const $$createField25_0 = $$createType25; + const $$createField26_0 = $$createType26; + const $$createField27_0 = $$createType27; + const $$createField28_0 = $$createType28; + const $$createField29_0 = $$createType29; + const $$createField30_0 = $$createType30; + const $$createField31_0 = $$createType31; + const $$createField32_0 = $$createType32; + const $$createField33_0 = $$createType33; + const $$createField34_0 = $$createType34; + const $$createField35_0 = $$createType35; + const $$createField36_0 = $$createType36; + const $$createField37_0 = $$createType37; + const $$createField38_0 = $$createType38; + const $$createField39_0 = $$createType39; + const $$createField40_0 = $$createType40; + const $$createField41_0 = $$createType41; + const $$createField42_0 = $$createType0; + const $$createField43_0 = $$createType42; + const $$createField44_0 = $$createType7; + const $$createField45_0 = $$createType43; + const $$createField46_0 = $$createType1; + const $$createField47_0 = $$createType44; + const $$createField48_0 = $$createType45; + const $$createField49_0 = $$createType46; + const $$createField50_0 = $$createType47; + const $$createField51_0 = $$createType15; + const $$createField52_0 = $$createType16; + const $$createField53_0 = $$createType16; + const $$createField54_0 = $$createType48; + const $$createField55_0 = $$createType49; + const $$createField56_0 = $$createType50; + const $$createField57_0 = $$createType51; + const $$createField58_0 = $$createType52; + const $$createField59_0 = $$createType17; + const $$createField60_0 = $$createType18; + const $$createField61_0 = $$createType18; + const $$createField62_0 = $$createType53; + const $$createField63_0 = $$createType54; + const $$createField64_0 = $$createType55; + const $$createField65_0 = $$createType56; + const $$createField66_0 = $$createType57; + const $$createField67_0 = $$createType23; + const $$createField68_0 = $$createType24; + const $$createField69_0 = $$createType24; + const $$createField70_0 = $$createType58; + const $$createField71_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField72_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField73_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField74_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField75_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField76_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField77_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField78_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField79_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField80_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField81_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField82_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField83_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField84_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField85_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField86_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField87_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField88_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField89_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField90_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField91_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField92_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField93_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField94_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField95_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField96_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField97_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField98_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField99_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField100_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField101_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField102_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField103_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField104_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField105_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField106_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField107_0 = $$createType1; + const $$createField108_0 = $$createType15; + const $$createField109_0 = $$createType17; + const $$createField110_0 = $$createType8; + const $$createField111_0 = $$createType16; + const $$createField112_0 = $$createType18; + const $$createField113_0 = $$createType1; + const $$createField114_0 = $$createType7; + const $$createField115_0 = $$createType8; + const $$createField116_0 = $$createType77; + const $$createField117_0 = $$createType78; + const $$createField118_0 = $$createType15; + const $$createField119_0 = $$createType16; + const $$createField120_0 = $$createType15; + const $$createField121_0 = $$createType18; + const $$createField122_0 = $$createType16; + const $$createField123_0 = $$createType53; + const $$createField124_0 = $$createType15; + const $$createField125_0 = $$createType16; + const $$createField126_0 = $$createType17; + const $$createField127_0 = $$createType18; + const $$createField128_0 = $$createType16; + const $$createField129_0 = $$createType18; + const $$createField130_0 = $$createType2; + const $$createField131_0 = $$createType42; + const $$createField132_0 = $$createType15; + const $$createField133_0 = $$createType79; + const $$createField134_0 = $$createType16; + const $$createField135_0 = $$createType23; + const $$createField136_0 = $$createType15; + const $$createField137_0 = $$createType18; + const $$createField138_0 = $$createType24; + const $$createField139_0 = $$createType16; + const $$createField140_0 = $$createType53; + const $$createField141_0 = $$createType16; + const $$createField142_0 = $$createType18; + const $$createField143_0 = $$createType48; + const $$createField144_0 = $$createType53; + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Bool" in $$parsedSource) { + $$parsedSource["Bool"] = $$createField0_0($$parsedSource["Bool"]); + } + if ("Int" in $$parsedSource) { + $$parsedSource["Int"] = $$createField1_0($$parsedSource["Int"]); + } + if ("Uint" in $$parsedSource) { + $$parsedSource["Uint"] = $$createField2_0($$parsedSource["Uint"]); + } + if ("Float" in $$parsedSource) { + $$parsedSource["Float"] = $$createField3_0($$parsedSource["Float"]); + } + if ("Complex" in $$parsedSource) { + $$parsedSource["Complex"] = $$createField4_0($$parsedSource["Complex"]); + } + if ("Byte" in $$parsedSource) { + $$parsedSource["Byte"] = $$createField5_0($$parsedSource["Byte"]); + } + if ("Rune" in $$parsedSource) { + $$parsedSource["Rune"] = $$createField6_0($$parsedSource["Rune"]); + } + if ("String" in $$parsedSource) { + $$parsedSource["String"] = $$createField7_0($$parsedSource["String"]); + } + if ("IntPtr" in $$parsedSource) { + $$parsedSource["IntPtr"] = $$createField8_0($$parsedSource["IntPtr"]); + } + if ("UintPtr" in $$parsedSource) { + $$parsedSource["UintPtr"] = $$createField9_0($$parsedSource["UintPtr"]); + } + if ("FloatPtr" in $$parsedSource) { + $$parsedSource["FloatPtr"] = $$createField10_0($$parsedSource["FloatPtr"]); + } + if ("ComplexPtr" in $$parsedSource) { + $$parsedSource["ComplexPtr"] = $$createField11_0($$parsedSource["ComplexPtr"]); + } + if ("StringPtr" in $$parsedSource) { + $$parsedSource["StringPtr"] = $$createField12_0($$parsedSource["StringPtr"]); + } + if ("NTM" in $$parsedSource) { + $$parsedSource["NTM"] = $$createField13_0($$parsedSource["NTM"]); + } + if ("NTMPtr" in $$parsedSource) { + $$parsedSource["NTMPtr"] = $$createField14_0($$parsedSource["NTMPtr"]); + } + if ("VTM" in $$parsedSource) { + $$parsedSource["VTM"] = $$createField15_0($$parsedSource["VTM"]); + } + if ("VTMPtr" in $$parsedSource) { + $$parsedSource["VTMPtr"] = $$createField16_0($$parsedSource["VTMPtr"]); + } + if ("PTM" in $$parsedSource) { + $$parsedSource["PTM"] = $$createField17_0($$parsedSource["PTM"]); + } + if ("PTMPtr" in $$parsedSource) { + $$parsedSource["PTMPtr"] = $$createField18_0($$parsedSource["PTMPtr"]); + } + if ("JTM" in $$parsedSource) { + $$parsedSource["JTM"] = $$createField19_0($$parsedSource["JTM"]); + } + if ("JTMPtr" in $$parsedSource) { + $$parsedSource["JTMPtr"] = $$createField20_0($$parsedSource["JTMPtr"]); + } + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField21_0($$parsedSource["A"]); + } + if ("APtr" in $$parsedSource) { + $$parsedSource["APtr"] = $$createField22_0($$parsedSource["APtr"]); + } + if ("TM" in $$parsedSource) { + $$parsedSource["TM"] = $$createField23_0($$parsedSource["TM"]); + } + if ("TMPtr" in $$parsedSource) { + $$parsedSource["TMPtr"] = $$createField24_0($$parsedSource["TMPtr"]); + } + if ("CI" in $$parsedSource) { + $$parsedSource["CI"] = $$createField25_0($$parsedSource["CI"]); + } + if ("CIPtr" in $$parsedSource) { + $$parsedSource["CIPtr"] = $$createField26_0($$parsedSource["CIPtr"]); + } + if ("EI" in $$parsedSource) { + $$parsedSource["EI"] = $$createField27_0($$parsedSource["EI"]); + } + if ("EIPtr" in $$parsedSource) { + $$parsedSource["EIPtr"] = $$createField28_0($$parsedSource["EIPtr"]); + } + if ("EV" in $$parsedSource) { + $$parsedSource["EV"] = $$createField29_0($$parsedSource["EV"]); + } + if ("EVPtr" in $$parsedSource) { + $$parsedSource["EVPtr"] = $$createField30_0($$parsedSource["EVPtr"]); + } + if ("EVP" in $$parsedSource) { + $$parsedSource["EVP"] = $$createField31_0($$parsedSource["EVP"]); + } + if ("EVPPtr" in $$parsedSource) { + $$parsedSource["EVPPtr"] = $$createField32_0($$parsedSource["EVPPtr"]); + } + if ("EP" in $$parsedSource) { + $$parsedSource["EP"] = $$createField33_0($$parsedSource["EP"]); + } + if ("EPPtr" in $$parsedSource) { + $$parsedSource["EPPtr"] = $$createField34_0($$parsedSource["EPPtr"]); + } + if ("EPP" in $$parsedSource) { + $$parsedSource["EPP"] = $$createField35_0($$parsedSource["EPP"]); + } + if ("EPPPtr" in $$parsedSource) { + $$parsedSource["EPPPtr"] = $$createField36_0($$parsedSource["EPPPtr"]); + } + if ("ECI" in $$parsedSource) { + $$parsedSource["ECI"] = $$createField37_0($$parsedSource["ECI"]); + } + if ("ECIPtr" in $$parsedSource) { + $$parsedSource["ECIPtr"] = $$createField38_0($$parsedSource["ECIPtr"]); + } + if ("EOI" in $$parsedSource) { + $$parsedSource["EOI"] = $$createField39_0($$parsedSource["EOI"]); + } + if ("EOIPtr" in $$parsedSource) { + $$parsedSource["EOIPtr"] = $$createField40_0($$parsedSource["EOIPtr"]); + } + if ("WT" in $$parsedSource) { + $$parsedSource["WT"] = $$createField41_0($$parsedSource["WT"]); + } + if ("WA" in $$parsedSource) { + $$parsedSource["WA"] = $$createField42_0($$parsedSource["WA"]); + } + if ("ST" in $$parsedSource) { + $$parsedSource["ST"] = $$createField43_0($$parsedSource["ST"]); + } + if ("SA" in $$parsedSource) { + $$parsedSource["SA"] = $$createField44_0($$parsedSource["SA"]); + } + if ("IntT" in $$parsedSource) { + $$parsedSource["IntT"] = $$createField45_0($$parsedSource["IntT"]); + } + if ("IntA" in $$parsedSource) { + $$parsedSource["IntA"] = $$createField46_0($$parsedSource["IntA"]); + } + if ("VT" in $$parsedSource) { + $$parsedSource["VT"] = $$createField47_0($$parsedSource["VT"]); + } + if ("VTPtr" in $$parsedSource) { + $$parsedSource["VTPtr"] = $$createField48_0($$parsedSource["VTPtr"]); + } + if ("VPT" in $$parsedSource) { + $$parsedSource["VPT"] = $$createField49_0($$parsedSource["VPT"]); + } + if ("VPTPtr" in $$parsedSource) { + $$parsedSource["VPTPtr"] = $$createField50_0($$parsedSource["VPTPtr"]); + } + if ("VA" in $$parsedSource) { + $$parsedSource["VA"] = $$createField51_0($$parsedSource["VA"]); + } + if ("VAPtr" in $$parsedSource) { + $$parsedSource["VAPtr"] = $$createField52_0($$parsedSource["VAPtr"]); + } + if ("VPA" in $$parsedSource) { + $$parsedSource["VPA"] = $$createField53_0($$parsedSource["VPA"]); + } + if ("VPAPtr" in $$parsedSource) { + $$parsedSource["VPAPtr"] = $$createField54_0($$parsedSource["VPAPtr"]); + } + if ("PT" in $$parsedSource) { + $$parsedSource["PT"] = $$createField55_0($$parsedSource["PT"]); + } + if ("PTPtr" in $$parsedSource) { + $$parsedSource["PTPtr"] = $$createField56_0($$parsedSource["PTPtr"]); + } + if ("PPT" in $$parsedSource) { + $$parsedSource["PPT"] = $$createField57_0($$parsedSource["PPT"]); + } + if ("PPTPtr" in $$parsedSource) { + $$parsedSource["PPTPtr"] = $$createField58_0($$parsedSource["PPTPtr"]); + } + if ("PA" in $$parsedSource) { + $$parsedSource["PA"] = $$createField59_0($$parsedSource["PA"]); + } + if ("PAPtr" in $$parsedSource) { + $$parsedSource["PAPtr"] = $$createField60_0($$parsedSource["PAPtr"]); + } + if ("PPA" in $$parsedSource) { + $$parsedSource["PPA"] = $$createField61_0($$parsedSource["PPA"]); + } + if ("PPAPtr" in $$parsedSource) { + $$parsedSource["PPAPtr"] = $$createField62_0($$parsedSource["PPAPtr"]); + } + if ("IT" in $$parsedSource) { + $$parsedSource["IT"] = $$createField63_0($$parsedSource["IT"]); + } + if ("ITPtr" in $$parsedSource) { + $$parsedSource["ITPtr"] = $$createField64_0($$parsedSource["ITPtr"]); + } + if ("IPT" in $$parsedSource) { + $$parsedSource["IPT"] = $$createField65_0($$parsedSource["IPT"]); + } + if ("IPTPtr" in $$parsedSource) { + $$parsedSource["IPTPtr"] = $$createField66_0($$parsedSource["IPTPtr"]); + } + if ("IA" in $$parsedSource) { + $$parsedSource["IA"] = $$createField67_0($$parsedSource["IA"]); + } + if ("IAPtr" in $$parsedSource) { + $$parsedSource["IAPtr"] = $$createField68_0($$parsedSource["IAPtr"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField69_0($$parsedSource["IPA"]); + } + if ("IPAPtr" in $$parsedSource) { + $$parsedSource["IPAPtr"] = $$createField70_0($$parsedSource["IPAPtr"]); + } + if ("TPR" in $$parsedSource) { + $$parsedSource["TPR"] = $$createField71_0($$parsedSource["TPR"]); + } + if ("TPRPtr" in $$parsedSource) { + $$parsedSource["TPRPtr"] = $$createField72_0($$parsedSource["TPRPtr"]); + } + if ("TPS" in $$parsedSource) { + $$parsedSource["TPS"] = $$createField73_0($$parsedSource["TPS"]); + } + if ("TPSPtr" in $$parsedSource) { + $$parsedSource["TPSPtr"] = $$createField74_0($$parsedSource["TPSPtr"]); + } + if ("TPT" in $$parsedSource) { + $$parsedSource["TPT"] = $$createField75_0($$parsedSource["TPT"]); + } + if ("TPTPtr" in $$parsedSource) { + $$parsedSource["TPTPtr"] = $$createField76_0($$parsedSource["TPTPtr"]); + } + if ("TPU" in $$parsedSource) { + $$parsedSource["TPU"] = $$createField77_0($$parsedSource["TPU"]); + } + if ("TPUPtr" in $$parsedSource) { + $$parsedSource["TPUPtr"] = $$createField78_0($$parsedSource["TPUPtr"]); + } + if ("TPV" in $$parsedSource) { + $$parsedSource["TPV"] = $$createField79_0($$parsedSource["TPV"]); + } + if ("TPVPtr" in $$parsedSource) { + $$parsedSource["TPVPtr"] = $$createField80_0($$parsedSource["TPVPtr"]); + } + if ("TPW" in $$parsedSource) { + $$parsedSource["TPW"] = $$createField81_0($$parsedSource["TPW"]); + } + if ("TPWPtr" in $$parsedSource) { + $$parsedSource["TPWPtr"] = $$createField82_0($$parsedSource["TPWPtr"]); + } + if ("TPX" in $$parsedSource) { + $$parsedSource["TPX"] = $$createField83_0($$parsedSource["TPX"]); + } + if ("TPXPtr" in $$parsedSource) { + $$parsedSource["TPXPtr"] = $$createField84_0($$parsedSource["TPXPtr"]); + } + if ("TPY" in $$parsedSource) { + $$parsedSource["TPY"] = $$createField85_0($$parsedSource["TPY"]); + } + if ("TPYPtr" in $$parsedSource) { + $$parsedSource["TPYPtr"] = $$createField86_0($$parsedSource["TPYPtr"]); + } + if ("TPZ" in $$parsedSource) { + $$parsedSource["TPZ"] = $$createField87_0($$parsedSource["TPZ"]); + } + if ("TPZPtr" in $$parsedSource) { + $$parsedSource["TPZPtr"] = $$createField88_0($$parsedSource["TPZPtr"]); + } + if ("GAR" in $$parsedSource) { + $$parsedSource["GAR"] = $$createField89_0($$parsedSource["GAR"]); + } + if ("GARPtr" in $$parsedSource) { + $$parsedSource["GARPtr"] = $$createField90_0($$parsedSource["GARPtr"]); + } + if ("GAS" in $$parsedSource) { + $$parsedSource["GAS"] = $$createField91_0($$parsedSource["GAS"]); + } + if ("GASPtr" in $$parsedSource) { + $$parsedSource["GASPtr"] = $$createField92_0($$parsedSource["GASPtr"]); + } + if ("GAT" in $$parsedSource) { + $$parsedSource["GAT"] = $$createField93_0($$parsedSource["GAT"]); + } + if ("GATPtr" in $$parsedSource) { + $$parsedSource["GATPtr"] = $$createField94_0($$parsedSource["GATPtr"]); + } + if ("GAU" in $$parsedSource) { + $$parsedSource["GAU"] = $$createField95_0($$parsedSource["GAU"]); + } + if ("GAUPtr" in $$parsedSource) { + $$parsedSource["GAUPtr"] = $$createField96_0($$parsedSource["GAUPtr"]); + } + if ("GAV" in $$parsedSource) { + $$parsedSource["GAV"] = $$createField97_0($$parsedSource["GAV"]); + } + if ("GAVPtr" in $$parsedSource) { + $$parsedSource["GAVPtr"] = $$createField98_0($$parsedSource["GAVPtr"]); + } + if ("GAW" in $$parsedSource) { + $$parsedSource["GAW"] = $$createField99_0($$parsedSource["GAW"]); + } + if ("GAWPtr" in $$parsedSource) { + $$parsedSource["GAWPtr"] = $$createField100_0($$parsedSource["GAWPtr"]); + } + if ("GAX" in $$parsedSource) { + $$parsedSource["GAX"] = $$createField101_0($$parsedSource["GAX"]); + } + if ("GAXPtr" in $$parsedSource) { + $$parsedSource["GAXPtr"] = $$createField102_0($$parsedSource["GAXPtr"]); + } + if ("GAY" in $$parsedSource) { + $$parsedSource["GAY"] = $$createField103_0($$parsedSource["GAY"]); + } + if ("GAYPtr" in $$parsedSource) { + $$parsedSource["GAYPtr"] = $$createField104_0($$parsedSource["GAYPtr"]); + } + if ("GAZ" in $$parsedSource) { + $$parsedSource["GAZ"] = $$createField105_0($$parsedSource["GAZ"]); + } + if ("GAZPtr" in $$parsedSource) { + $$parsedSource["GAZPtr"] = $$createField106_0($$parsedSource["GAZPtr"]); + } + if ("GACi" in $$parsedSource) { + $$parsedSource["GACi"] = $$createField107_0($$parsedSource["GACi"]); + } + if ("GACV" in $$parsedSource) { + $$parsedSource["GACV"] = $$createField108_0($$parsedSource["GACV"]); + } + if ("GACP" in $$parsedSource) { + $$parsedSource["GACP"] = $$createField109_0($$parsedSource["GACP"]); + } + if ("GACiPtr" in $$parsedSource) { + $$parsedSource["GACiPtr"] = $$createField110_0($$parsedSource["GACiPtr"]); + } + if ("GACVPtr" in $$parsedSource) { + $$parsedSource["GACVPtr"] = $$createField111_0($$parsedSource["GACVPtr"]); + } + if ("GACPPtr" in $$parsedSource) { + $$parsedSource["GACPPtr"] = $$createField112_0($$parsedSource["GACPPtr"]); + } + if ("GABi" in $$parsedSource) { + $$parsedSource["GABi"] = $$createField113_0($$parsedSource["GABi"]); + } + if ("GABs" in $$parsedSource) { + $$parsedSource["GABs"] = $$createField114_0($$parsedSource["GABs"]); + } + if ("GABiPtr" in $$parsedSource) { + $$parsedSource["GABiPtr"] = $$createField115_0($$parsedSource["GABiPtr"]); + } + if ("GABT" in $$parsedSource) { + $$parsedSource["GABT"] = $$createField116_0($$parsedSource["GABT"]); + } + if ("GABTPtr" in $$parsedSource) { + $$parsedSource["GABTPtr"] = $$createField117_0($$parsedSource["GABTPtr"]); + } + if ("GAGT" in $$parsedSource) { + $$parsedSource["GAGT"] = $$createField118_0($$parsedSource["GAGT"]); + } + if ("GAGTPtr" in $$parsedSource) { + $$parsedSource["GAGTPtr"] = $$createField119_0($$parsedSource["GAGTPtr"]); + } + if ("GANBV" in $$parsedSource) { + $$parsedSource["GANBV"] = $$createField120_0($$parsedSource["GANBV"]); + } + if ("GANBP" in $$parsedSource) { + $$parsedSource["GANBP"] = $$createField121_0($$parsedSource["GANBP"]); + } + if ("GANBVPtr" in $$parsedSource) { + $$parsedSource["GANBVPtr"] = $$createField122_0($$parsedSource["GANBVPtr"]); + } + if ("GANBPPtr" in $$parsedSource) { + $$parsedSource["GANBPPtr"] = $$createField123_0($$parsedSource["GANBPPtr"]); + } + if ("GAPlV1" in $$parsedSource) { + $$parsedSource["GAPlV1"] = $$createField124_0($$parsedSource["GAPlV1"]); + } + if ("GAPlV2" in $$parsedSource) { + $$parsedSource["GAPlV2"] = $$createField125_0($$parsedSource["GAPlV2"]); + } + if ("GAPlP1" in $$parsedSource) { + $$parsedSource["GAPlP1"] = $$createField126_0($$parsedSource["GAPlP1"]); + } + if ("GAPlP2" in $$parsedSource) { + $$parsedSource["GAPlP2"] = $$createField127_0($$parsedSource["GAPlP2"]); + } + if ("GAPlVPtr" in $$parsedSource) { + $$parsedSource["GAPlVPtr"] = $$createField128_0($$parsedSource["GAPlVPtr"]); + } + if ("GAPlPPtr" in $$parsedSource) { + $$parsedSource["GAPlPPtr"] = $$createField129_0($$parsedSource["GAPlPPtr"]); + } + if ("GAMi" in $$parsedSource) { + $$parsedSource["GAMi"] = $$createField130_0($$parsedSource["GAMi"]); + } + if ("GAMS" in $$parsedSource) { + $$parsedSource["GAMS"] = $$createField131_0($$parsedSource["GAMS"]); + } + if ("GAMV" in $$parsedSource) { + $$parsedSource["GAMV"] = $$createField132_0($$parsedSource["GAMV"]); + } + if ("GAMSPtr" in $$parsedSource) { + $$parsedSource["GAMSPtr"] = $$createField133_0($$parsedSource["GAMSPtr"]); + } + if ("GAMVPtr" in $$parsedSource) { + $$parsedSource["GAMVPtr"] = $$createField134_0($$parsedSource["GAMVPtr"]); + } + if ("GAII" in $$parsedSource) { + $$parsedSource["GAII"] = $$createField135_0($$parsedSource["GAII"]); + } + if ("GAIV" in $$parsedSource) { + $$parsedSource["GAIV"] = $$createField136_0($$parsedSource["GAIV"]); + } + if ("GAIP" in $$parsedSource) { + $$parsedSource["GAIP"] = $$createField137_0($$parsedSource["GAIP"]); + } + if ("GAIIPtr" in $$parsedSource) { + $$parsedSource["GAIIPtr"] = $$createField138_0($$parsedSource["GAIIPtr"]); + } + if ("GAIVPtr" in $$parsedSource) { + $$parsedSource["GAIVPtr"] = $$createField139_0($$parsedSource["GAIVPtr"]); + } + if ("GAIPPtr" in $$parsedSource) { + $$parsedSource["GAIPPtr"] = $$createField140_0($$parsedSource["GAIPPtr"]); + } + if ("GAPrV" in $$parsedSource) { + $$parsedSource["GAPrV"] = $$createField141_0($$parsedSource["GAPrV"]); + } + if ("GAPrP" in $$parsedSource) { + $$parsedSource["GAPrP"] = $$createField142_0($$parsedSource["GAPrP"]); + } + if ("GAPrVPtr" in $$parsedSource) { + $$parsedSource["GAPrVPtr"] = $$createField143_0($$parsedSource["GAPrVPtr"]); + } + if ("GAPrPPtr" in $$parsedSource) { + $$parsedSource["GAPrPPtr"] = $$createField144_0($$parsedSource["GAPrPPtr"]); + } + return new Maps($$parsedSource as Partial>); + }; + } +} + +export type MixedCstrAlias = X; + +export type NonBasicCstrAlias = V; + +export type PointableCstrAlias = W; + +export type PointerAlias = PointerTextMarshaler; + +export type PointerTextMarshaler = string; + +export type StringAlias = string; + +export type StringType = string; + +export type ValueAlias = ValueTextMarshaler; + +export type ValueTextMarshaler = string; + +// Private type creation functions +const $$createType0 = $Create.Map($Create.Any, $Create.Any); +const $$createType1 = $Create.Map($Create.Any, $Create.Any); +const $$createType2 = $Create.Map($Create.Any, $Create.Any); +const $$createType3 = $Create.Map($Create.Any, $Create.Any); +const $$createType4 = $Create.Map($Create.Any, $Create.Any); +const $$createType5 = $Create.Map($Create.Any, $Create.Any); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Map($Create.Any, $Create.Any); +const $$createType8 = $Create.Map($Create.Any, $Create.Any); +const $$createType9 = $Create.Map($Create.Any, $Create.Any); +const $$createType10 = $Create.Map($Create.Any, $Create.Any); +const $$createType11 = $Create.Map($Create.Any, $Create.Any); +const $$createType12 = $Create.Map($Create.Any, $Create.Any); +const $$createType13 = $Create.Map($Create.Any, $Create.Any); +const $$createType14 = $Create.Map($Create.Any, $Create.Any); +const $$createType15 = $Create.Map($Create.Any, $Create.Any); +const $$createType16 = $Create.Map($Create.Any, $Create.Any); +const $$createType17 = $Create.Map($Create.Any, $Create.Any); +const $$createType18 = $Create.Map($Create.Any, $Create.Any); +const $$createType19 = $Create.Map($Create.Any, $Create.Any); +const $$createType20 = $Create.Map($Create.Any, $Create.Any); +const $$createType21 = $Create.Map($Create.Any, $Create.Any); +const $$createType22 = $Create.Map($Create.Any, $Create.Any); +const $$createType23 = $Create.Map($Create.Any, $Create.Any); +const $$createType24 = $Create.Map($Create.Any, $Create.Any); +const $$createType25 = $Create.Map($Create.Any, $Create.Any); +const $$createType26 = $Create.Map($Create.Any, $Create.Any); +const $$createType27 = $Create.Map($Create.Any, $Create.Any); +const $$createType28 = $Create.Map($Create.Any, $Create.Any); +const $$createType29 = $Create.Map($Create.Any, $Create.Any); +const $$createType30 = $Create.Map($Create.Any, $Create.Any); +const $$createType31 = $Create.Map($Create.Any, $Create.Any); +const $$createType32 = $Create.Map($Create.Any, $Create.Any); +const $$createType33 = $Create.Map($Create.Any, $Create.Any); +const $$createType34 = $Create.Map($Create.Any, $Create.Any); +const $$createType35 = $Create.Map($Create.Any, $Create.Any); +const $$createType36 = $Create.Map($Create.Any, $Create.Any); +const $$createType37 = $Create.Map($Create.Any, $Create.Any); +const $$createType38 = $Create.Map($Create.Any, $Create.Any); +const $$createType39 = $Create.Map($Create.Any, $Create.Any); +const $$createType40 = $Create.Map($Create.Any, $Create.Any); +const $$createType41 = $Create.Map($Create.Any, $Create.Any); +const $$createType42 = $Create.Map($Create.Any, $Create.Any); +const $$createType43 = $Create.Map($Create.Any, $Create.Any); +const $$createType44 = $Create.Map($Create.Any, $Create.Any); +const $$createType45 = $Create.Map($Create.Any, $Create.Any); +const $$createType46 = $Create.Map($Create.Any, $Create.Any); +const $$createType47 = $Create.Map($Create.Any, $Create.Any); +const $$createType48 = $Create.Map($Create.Any, $Create.Any); +const $$createType49 = $Create.Map($Create.Any, $Create.Any); +const $$createType50 = $Create.Map($Create.Any, $Create.Any); +const $$createType51 = $Create.Map($Create.Any, $Create.Any); +const $$createType52 = $Create.Map($Create.Any, $Create.Any); +const $$createType53 = $Create.Map($Create.Any, $Create.Any); +const $$createType54 = $Create.Map($Create.Any, $Create.Any); +const $$createType55 = $Create.Map($Create.Any, $Create.Any); +const $$createType56 = $Create.Map($Create.Any, $Create.Any); +const $$createType57 = $Create.Map($Create.Any, $Create.Any); +const $$createType58 = $Create.Map($Create.Any, $Create.Any); +const $$createType59 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType60 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType61 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType62 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType63 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType64 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType65 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType66 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType67 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType68 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType69 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType70 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType71 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType72 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType73 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType74 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType75 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType76 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType77 = $Create.Map($Create.Any, $Create.Any); +const $$createType78 = $Create.Map($Create.Any, $Create.Any); +const $$createType79 = $Create.Map($Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts new file mode 100644 index 000000000..bd5e88c7b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts @@ -0,0 +1,19 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>> { + return $Call.ByID(4021345184).then(($result: any) => { + return $$createType0($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Maps.createFrom($Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts new file mode 100644 index 000000000..bbe49e32f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts @@ -0,0 +1,36 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Data, + ImplicitNonMarshaler, + NonMarshaler +} from "./models.js"; + +export type { + AliasJsonMarshaler, + AliasMarshaler, + AliasNonMarshaler, + AliasTextMarshaler, + ImplicitJsonButText, + ImplicitJsonMarshaler, + ImplicitMarshaler, + ImplicitNonJson, + ImplicitNonText, + ImplicitTextButJson, + ImplicitTextMarshaler, + PointerJsonMarshaler, + PointerMarshaler, + PointerTextMarshaler, + UnderlyingJsonMarshaler, + UnderlyingMarshaler, + UnderlyingTextMarshaler, + ValueJsonMarshaler, + ValueMarshaler, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts new file mode 100644 index 000000000..d8db23862 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts @@ -0,0 +1,545 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + */ +export type AliasJsonMarshaler = any; + +/** + * any + */ +export type AliasMarshaler = any; + +/** + * struct{} + */ +export interface AliasNonMarshaler { +} + +/** + * string + */ +export type AliasTextMarshaler = string; + +export class Data { + "NM": NonMarshaler; + + /** + * NonMarshaler | null + */ + "NMPtr": NonMarshaler | null; + "VJM": ValueJsonMarshaler; + + /** + * ValueJsonMarshaler | null + */ + "VJMPtr": ValueJsonMarshaler | null; + "PJM": PointerJsonMarshaler; + + /** + * PointerJsonMarshaler | null + */ + "PJMPtr": PointerJsonMarshaler | null; + "VTM": ValueTextMarshaler; + + /** + * ValueTextMarshaler | null + */ + "VTMPtr": ValueTextMarshaler | null; + "PTM": PointerTextMarshaler; + + /** + * PointerTextMarshaler | null + */ + "PTMPtr": PointerTextMarshaler | null; + "VM": ValueMarshaler; + + /** + * ValueMarshaler | null + */ + "VMPtr": ValueMarshaler | null; + "PM": PointerMarshaler; + + /** + * PointerMarshaler | null + */ + "PMPtr": PointerMarshaler | null; + "UJM": UnderlyingJsonMarshaler; + + /** + * UnderlyingJsonMarshaler | null + */ + "UJMPtr": UnderlyingJsonMarshaler | null; + "UTM": UnderlyingTextMarshaler; + + /** + * UnderlyingTextMarshaler | null + */ + "UTMPtr": UnderlyingTextMarshaler | null; + "UM": UnderlyingMarshaler; + + /** + * UnderlyingMarshaler | null + */ + "UMPtr": UnderlyingMarshaler | null; + + /** + * any + */ + "JM": any; + + /** + * any | null + */ + "JMPtr": any | null; + + /** + * string + */ + "TM": string; + + /** + * string | null + */ + "TMPtr": string | null; + + /** + * any + */ + "CJM": any; + + /** + * any | null + */ + "CJMPtr": any | null; + + /** + * string + */ + "CTM": string; + + /** + * string | null + */ + "CTMPtr": string | null; + + /** + * any + */ + "CM": any; + + /** + * any | null + */ + "CMPtr": any | null; + "ANM": AliasNonMarshaler; + + /** + * AliasNonMarshaler | null + */ + "ANMPtr": AliasNonMarshaler | null; + "AJM": AliasJsonMarshaler; + + /** + * AliasJsonMarshaler | null + */ + "AJMPtr": AliasJsonMarshaler | null; + "ATM": AliasTextMarshaler; + + /** + * AliasTextMarshaler | null + */ + "ATMPtr": AliasTextMarshaler | null; + "AM": AliasMarshaler; + + /** + * AliasMarshaler | null + */ + "AMPtr": AliasMarshaler | null; + "ImJM": ImplicitJsonMarshaler; + + /** + * ImplicitJsonMarshaler | null + */ + "ImJMPtr": ImplicitJsonMarshaler | null; + "ImTM": ImplicitTextMarshaler; + + /** + * ImplicitTextMarshaler | null + */ + "ImTMPtr": ImplicitTextMarshaler | null; + "ImM": ImplicitMarshaler; + + /** + * ImplicitMarshaler | null + */ + "ImMPtr": ImplicitMarshaler | null; + "ImNJ": ImplicitNonJson; + + /** + * ImplicitNonJson | null + */ + "ImNJPtr": ImplicitNonJson | null; + "ImNT": ImplicitNonText; + + /** + * ImplicitNonText | null + */ + "ImNTPtr": ImplicitNonText | null; + "ImNM": ImplicitNonMarshaler; + + /** + * ImplicitNonMarshaler | null + */ + "ImNMPtr": ImplicitNonMarshaler | null; + "ImJbT": ImplicitJsonButText; + + /** + * ImplicitJsonButText | null + */ + "ImJbTPtr": ImplicitJsonButText | null; + "ImTbJ": ImplicitTextButJson; + + /** + * ImplicitTextButJson | null + */ + "ImTbJPtr": ImplicitTextButJson | null; + + /** Creates a new Data instance. */ + constructor($$source: Partial = {}) { + if (!("NM" in $$source)) { + this["NM"] = (new NonMarshaler()); + } + if (!("NMPtr" in $$source)) { + this["NMPtr"] = null; + } + if (!("VJM" in $$source)) { + this["VJM"] = null; + } + if (!("VJMPtr" in $$source)) { + this["VJMPtr"] = null; + } + if (!("PJM" in $$source)) { + this["PJM"] = null; + } + if (!("PJMPtr" in $$source)) { + this["PJMPtr"] = null; + } + if (!("VTM" in $$source)) { + this["VTM"] = ""; + } + if (!("VTMPtr" in $$source)) { + this["VTMPtr"] = null; + } + if (!("PTM" in $$source)) { + this["PTM"] = ""; + } + if (!("PTMPtr" in $$source)) { + this["PTMPtr"] = null; + } + if (!("VM" in $$source)) { + this["VM"] = null; + } + if (!("VMPtr" in $$source)) { + this["VMPtr"] = null; + } + if (!("PM" in $$source)) { + this["PM"] = null; + } + if (!("PMPtr" in $$source)) { + this["PMPtr"] = null; + } + if (!("UJM" in $$source)) { + this["UJM"] = null; + } + if (!("UJMPtr" in $$source)) { + this["UJMPtr"] = null; + } + if (!("UTM" in $$source)) { + this["UTM"] = ""; + } + if (!("UTMPtr" in $$source)) { + this["UTMPtr"] = null; + } + if (!("UM" in $$source)) { + this["UM"] = null; + } + if (!("UMPtr" in $$source)) { + this["UMPtr"] = null; + } + if (!("JM" in $$source)) { + this["JM"] = null; + } + if (!("JMPtr" in $$source)) { + this["JMPtr"] = null; + } + if (!("TM" in $$source)) { + this["TM"] = ""; + } + if (!("TMPtr" in $$source)) { + this["TMPtr"] = null; + } + if (!("CJM" in $$source)) { + this["CJM"] = null; + } + if (!("CJMPtr" in $$source)) { + this["CJMPtr"] = null; + } + if (!("CTM" in $$source)) { + this["CTM"] = ""; + } + if (!("CTMPtr" in $$source)) { + this["CTMPtr"] = null; + } + if (!("CM" in $$source)) { + this["CM"] = null; + } + if (!("CMPtr" in $$source)) { + this["CMPtr"] = null; + } + if (!("ANM" in $$source)) { + this["ANM"] = {}; + } + if (!("ANMPtr" in $$source)) { + this["ANMPtr"] = null; + } + if (!("AJM" in $$source)) { + this["AJM"] = null; + } + if (!("AJMPtr" in $$source)) { + this["AJMPtr"] = null; + } + if (!("ATM" in $$source)) { + this["ATM"] = ""; + } + if (!("ATMPtr" in $$source)) { + this["ATMPtr"] = null; + } + if (!("AM" in $$source)) { + this["AM"] = null; + } + if (!("AMPtr" in $$source)) { + this["AMPtr"] = null; + } + if (!("ImJM" in $$source)) { + this["ImJM"] = null; + } + if (!("ImJMPtr" in $$source)) { + this["ImJMPtr"] = null; + } + if (!("ImTM" in $$source)) { + this["ImTM"] = ""; + } + if (!("ImTMPtr" in $$source)) { + this["ImTMPtr"] = null; + } + if (!("ImM" in $$source)) { + this["ImM"] = null; + } + if (!("ImMPtr" in $$source)) { + this["ImMPtr"] = null; + } + if (!("ImNJ" in $$source)) { + this["ImNJ"] = ""; + } + if (!("ImNJPtr" in $$source)) { + this["ImNJPtr"] = null; + } + if (!("ImNT" in $$source)) { + this["ImNT"] = null; + } + if (!("ImNTPtr" in $$source)) { + this["ImNTPtr"] = null; + } + if (!("ImNM" in $$source)) { + this["ImNM"] = (new ImplicitNonMarshaler()); + } + if (!("ImNMPtr" in $$source)) { + this["ImNMPtr"] = null; + } + if (!("ImJbT" in $$source)) { + this["ImJbT"] = null; + } + if (!("ImJbTPtr" in $$source)) { + this["ImJbTPtr"] = null; + } + if (!("ImTbJ" in $$source)) { + this["ImTbJ"] = null; + } + if (!("ImTbJPtr" in $$source)) { + this["ImTbJPtr"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Data instance from a string or object. + */ + static createFrom($$source: any = {}): Data { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField48_0 = $$createType2; + const $$createField49_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("NM" in $$parsedSource) { + $$parsedSource["NM"] = $$createField0_0($$parsedSource["NM"]); + } + if ("NMPtr" in $$parsedSource) { + $$parsedSource["NMPtr"] = $$createField1_0($$parsedSource["NMPtr"]); + } + if ("ImNM" in $$parsedSource) { + $$parsedSource["ImNM"] = $$createField48_0($$parsedSource["ImNM"]); + } + if ("ImNMPtr" in $$parsedSource) { + $$parsedSource["ImNMPtr"] = $$createField49_0($$parsedSource["ImNMPtr"]); + } + return new Data($$parsedSource as Partial); + } +} + +/** + * any + */ +export type ImplicitJsonButText = any; + +/** + * any + */ +export type ImplicitJsonMarshaler = any; + +/** + * any + */ +export type ImplicitMarshaler = any; + +/** + * string + */ +export type ImplicitNonJson = string; + +/** + * class{ Marshaler, TextMarshaler } + */ +export class ImplicitNonMarshaler { + "Marshaler": json$0.Marshaler; + "TextMarshaler": encoding$0.TextMarshaler; + + /** Creates a new ImplicitNonMarshaler instance. */ + constructor($$source: Partial = {}) { + if (!("Marshaler" in $$source)) { + this["Marshaler"] = null; + } + if (!("TextMarshaler" in $$source)) { + this["TextMarshaler"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new ImplicitNonMarshaler instance from a string or object. + */ + static createFrom($$source: any = {}): ImplicitNonMarshaler { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new ImplicitNonMarshaler($$parsedSource as Partial); + } +} + +/** + * any + */ +export type ImplicitNonText = any; + +/** + * any + */ +export type ImplicitTextButJson = any; + +/** + * string + */ +export type ImplicitTextMarshaler = string; + +/** + * class {} + */ +export class NonMarshaler { + + /** Creates a new NonMarshaler instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new NonMarshaler instance from a string or object. + */ + static createFrom($$source: any = {}): NonMarshaler { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new NonMarshaler($$parsedSource as Partial); + } +} + +/** + * any + */ +export type PointerJsonMarshaler = any; + +/** + * any + */ +export type PointerMarshaler = any; + +/** + * string + */ +export type PointerTextMarshaler = string; + +/** + * any + */ +export type UnderlyingJsonMarshaler = any; + +/** + * any + */ +export type UnderlyingMarshaler = any; + +/** + * string + */ +export type UnderlyingTextMarshaler = string; + +/** + * any + */ +export type ValueJsonMarshaler = any; + +/** + * any + */ +export type ValueMarshaler = any; + +/** + * string + */ +export type ValueTextMarshaler = string; + +// Private type creation functions +const $$createType0 = NonMarshaler.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = ImplicitNonMarshaler.createFrom; +const $$createType3 = $Create.Nullable($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts new file mode 100644 index 000000000..342fcf3c4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts @@ -0,0 +1,19 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Data> { + return $Call.ByID(4021345184).then(($result: any) => { + return $$createType0($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Data.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts new file mode 100644 index 000000000..19d990dbf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts @@ -0,0 +1,14 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +export { + HowDifferent, + Impersonator, + Person, + PrivatePerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts new file mode 100644 index 000000000..4cb328e1e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts @@ -0,0 +1,163 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + */ +export class HowDifferent { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": { [_: string]: How }[]; + + /** Creates a new HowDifferent instance. */ + constructor($$source: Partial> = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class HowDifferent. + */ + static createFrom($$createParamHow: (source: any) => How): ($$source?: any) => HowDifferent { + const $$createField1_0 = $$createType1($$createParamHow); + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new HowDifferent($$parsedSource as Partial>); + }; + } +} + +/** + * Impersonator gets their fields from other people. + */ +export const Impersonator = other$0.OtherPerson; + +/** + * Impersonator gets their fields from other people. + */ +export type Impersonator = other$0.OtherPerson; + +/** + * Person is not a number. + */ +export class Person { + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField1_0($$parsedSource["Friends"]); + } + return new Person($$parsedSource as Partial); + } +} + +export class personImpl { + /** + * Nickname conceals a person's identity. + */ + "Nickname": string; + + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; + + /** Creates a new personImpl instance. */ + constructor($$source: Partial = {}) { + if (!("Nickname" in $$source)) { + this["Nickname"] = ""; + } + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new personImpl instance from a string or object. + */ + static createFrom($$source: any = {}): personImpl { + const $$createField2_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField2_0($$parsedSource["Friends"]); + } + return new personImpl($$parsedSource as Partial); + } +} + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export const PrivatePerson = personImpl; + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export type PrivatePerson = personImpl; + +// Private type creation functions +const $$createType0 = ($$createParamHow: any) => $Create.Map($Create.Any, $$createParamHow); +const $$createType1 = ($$createParamHow: any) => $Create.Array($$createType0($$createParamHow)); +const $$createType2 = other$0.OtherPerson.createFrom($Create.Any); +const $$createType3 = $Create.Array($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts new file mode 100644 index 000000000..d2d973b28 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +export { + OtherPerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts new file mode 100644 index 000000000..711735a2b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts @@ -0,0 +1,52 @@ +// 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 "/wails/runtime.js"; + +/** + * OtherPerson is like a person, but different. + */ +export class OtherPerson { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": T[]; + + /** Creates a new OtherPerson instance. */ + constructor($$source: Partial> = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class OtherPerson. + */ + static createFrom($$createParamT: (source: any) => T): ($$source?: any) => OtherPerson { + const $$createField1_0 = $$createType0($$createParamT); + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new OtherPerson($$parsedSource as Partial>); + }; + } +} + +// Private type creation functions +const $$createType0 = ($$createParamT: any) => $Create.Array($$createParamT); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts new file mode 100644 index 000000000..da11f7f2f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(3606939272); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts new file mode 100644 index 000000000..9ef257343 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts @@ -0,0 +1,39 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]> { + return $Call.ByID(2124352079).then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + }); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(4281222271); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.HowDifferent.createFrom($Create.Any); +const $$createType2 = $models.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts new file mode 100644 index 000000000..a5fe5368f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(3566862802); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts new file mode 100644 index 000000000..615eae691 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts @@ -0,0 +1,39 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]> { + return $Call.ByID(2590614085).then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + }); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(773650321); +} + +// Private type creation functions +const $$createType0 = nobindingshere$0.Person.createFrom; +const $$createType1 = nobindingshere$0.HowDifferent.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts new file mode 100644 index 000000000..83ca81acc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet($0: string): $CancellablePromise { + return $Call.ByID(1411160069, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts new file mode 100644 index 000000000..e69bebf3b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts new file mode 100644 index 000000000..62283209b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts new file mode 100644 index 000000000..6c902629c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts new file mode 100644 index 000000000..62283209b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts new file mode 100644 index 000000000..6c902629c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts new file mode 100644 index 000000000..88cafa9d1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts new file mode 100644 index 000000000..d76f68d9d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +export class Person { + "Name": string; + "Address": services$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts new file mode 100644 index 000000000..b8c293ad7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(3568225479).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts new file mode 100644 index 000000000..c8ff6e4db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts @@ -0,0 +1,209 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByID(3862002418, $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByID(2424639793, $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByID(3132595881, $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByID(3306292566, $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1754277916, $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1909469092, $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(4251088558, $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1343888303, $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2205561041, $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByID(572240879, $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2189402897, $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByID(642881729, $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1066151743, $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByID(2718999663, $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number }): $CancellablePromise { + return $Call.ByID(2386486356, $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null }): $CancellablePromise { + return $Call.ByID(2163571325, $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] }): $CancellablePromise { + return $Call.ByID(2900172572, $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] }): $CancellablePromise<{ [_: `${number}`]: number[] }> { + return $Call.ByID(881980169, $in).then(($result: any) => { + return $$createType1($result); + }); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByID(1075577233); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByID(3589606958, $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByID(224675106, $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByID(2124953624, $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByID(3516977899, $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByID(229603958, $in); +} + +export function StringArrayInputNamedOutput($in: string[]): $CancellablePromise { + return $Call.ByID(3678582682, $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputNamedOutputs($in: string[]): $CancellablePromise { + return $Call.ByID(319259595, $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringArrayOut($in: string[]): $CancellablePromise { + return $Call.ByID(383995060, $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringOut($in: string[]): $CancellablePromise { + return $Call.ByID(1091960237, $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByID(3835643147, $in).then(($result: any) => { + return $$createType3($result); + }); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByID(2447692557, $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByID(2943477349, $in).then(($result: any) => { + return $$createType4($result); + }); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(3401034892, $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1236957573, $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(1160383782, $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1739300671, $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(793803239, $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1403757716, $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2988345717, $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(518250834, $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2836661285, $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1367187362, $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts new file mode 100644 index 000000000..cd282c90c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +export class Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + this["Parent"] = null; + } + if (!("Details" in $$source)) { + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts new file mode 100644 index 000000000..c8ff6e4db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts @@ -0,0 +1,209 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByID(3862002418, $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByID(2424639793, $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByID(3132595881, $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByID(3306292566, $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1754277916, $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1909469092, $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(4251088558, $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1343888303, $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2205561041, $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByID(572240879, $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2189402897, $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByID(642881729, $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1066151743, $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByID(2718999663, $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number }): $CancellablePromise { + return $Call.ByID(2386486356, $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null }): $CancellablePromise { + return $Call.ByID(2163571325, $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] }): $CancellablePromise { + return $Call.ByID(2900172572, $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] }): $CancellablePromise<{ [_: `${number}`]: number[] }> { + return $Call.ByID(881980169, $in).then(($result: any) => { + return $$createType1($result); + }); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByID(1075577233); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByID(3589606958, $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByID(224675106, $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByID(2124953624, $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByID(3516977899, $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByID(229603958, $in); +} + +export function StringArrayInputNamedOutput($in: string[]): $CancellablePromise { + return $Call.ByID(3678582682, $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputNamedOutputs($in: string[]): $CancellablePromise { + return $Call.ByID(319259595, $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringArrayOut($in: string[]): $CancellablePromise { + return $Call.ByID(383995060, $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringOut($in: string[]): $CancellablePromise { + return $Call.ByID(1091960237, $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByID(3835643147, $in).then(($result: any) => { + return $$createType3($result); + }); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByID(2447692557, $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByID(2943477349, $in).then(($result: any) => { + return $$createType4($result); + }); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(3401034892, $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1236957573, $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(1160383782, $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1739300671, $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(793803239, $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1403757716, $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2988345717, $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(518250834, $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2836661285, $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1367187362, $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts new file mode 100644 index 000000000..cd282c90c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +export class Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + this["Parent"] = null; + } + if (!("Details" in $$source)) { + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts new file mode 100644 index 000000000..6e6ac2007 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts new file mode 100644 index 000000000..6e6ac2007 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts new file mode 100644 index 000000000..88cafa9d1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts new file mode 100644 index 000000000..f6eee9de8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts @@ -0,0 +1,47 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + */ +export class Person { + "Name": string; + "Address": services$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts new file mode 100644 index 000000000..ec098d45a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(1491748400).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/warnings.log b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=false/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/index.ts new file mode 100644 index 000000000..ba2885969 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + TextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/json/index.ts new file mode 100644 index 000000000..00ec01151 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/json/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Marshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/json/models.ts new file mode 100644 index 000000000..8cd1a164f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/json/models.ts @@ -0,0 +1,12 @@ +// 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 "/wails/runtime.js"; + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + */ +export type Marshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/models.ts new file mode 100644 index 000000000..235dfce3e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/encoding/models.ts @@ -0,0 +1,14 @@ +// 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 "/wails/runtime.js"; + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + */ +export type TextMarshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts new file mode 100644 index 000000000..b33d68383 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts @@ -0,0 +1,82 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + */ +export function Get(aliasValue: $models.Alias): $CancellablePromise<$models.Person> { + return $Call.ByName("main.GreetService.Get", aliasValue).then(($result: any) => { + return $$createType0($result); + }); +} + +/** + * Apparently, aliases are all the rage right now. + */ +export function GetButAliased(p: $models.AliasedPerson): $CancellablePromise<$models.StrangelyAliasedPerson> { + return $Call.ByName("main.GreetService.GetButAliased", p).then(($result: any) => { + return $$createType0($result); + }); +} + +/** + * Get someone quite different. + */ +export function GetButDifferent(): $CancellablePromise<$models.GenericPerson> { + return $Call.ByName("main.GreetService.GetButDifferent").then(($result: any) => { + return $$createType1($result); + }); +} + +export function GetButForeignPrivateAlias(): $CancellablePromise { + return $Call.ByName("main.GreetService.GetButForeignPrivateAlias").then(($result: any) => { + return $$createType2($result); + }); +} + +export function GetButGenericAliases(): $CancellablePromise<$models.AliasGroup> { + return $Call.ByName("main.GreetService.GetButGenericAliases").then(($result: any) => { + return $$createType3($result); + }); +} + +/** + * Greet a lot of unusual things. + */ +export function Greet($0: $models.EmptyAliasStruct, $1: $models.EmptyStruct): $CancellablePromise<$models.AliasStruct> { + return $Call.ByName("main.GreetService.Greet", $0, $1).then(($result: any) => { + return $$createType7($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.GenericPerson.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; +const $$createType3 = $models.AliasGroup.createFrom; +const $$createType4 = $Create.Array($Create.Any); +const $$createType5 = $Create.Array($Create.Any); +const $$createType6 = $Create.Struct({ + "NoMoreIdeas": $$createType5, +}); +const $$createType7 = $Create.Struct({ + "Foo": $$createType4, + "Other": $$createType6, +}); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts new file mode 100644 index 000000000..13f61da0f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + AliasGroup, + AliasedPerson, + EmptyStruct, + GenericPerson, + GenericPersonAlias, + IndirectPersonAlias, + Person, + StrangelyAliasedPerson, + TPIndirectPersonAlias +} from "./models.js"; + +export type { + Alias, + AliasStruct, + EmptyAliasStruct, + GenericAlias, + GenericMapAlias, + GenericPtrAlias, + OtherAliasStruct +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts new file mode 100644 index 000000000..63ca43914 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts @@ -0,0 +1,290 @@ +// 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 "/wails/runtime.js"; + +/** + * A nice type Alias. + */ +export type Alias = number; + +/** + * A class whose fields have various aliased types. + */ +export class AliasGroup { + "GAi": GenericAlias; + "GAP": GenericAlias>; + "GPAs": GenericPtrAlias; + "GPAP": GenericPtrAlias>; + "GMA": GenericMapAlias; + "GPA": GenericPersonAlias; + "IPA": IndirectPersonAlias; + "TPIPA": TPIndirectPersonAlias; + + /** Creates a new AliasGroup instance. */ + constructor($$source: Partial = {}) { + if (!("GAi" in $$source)) { + this["GAi"] = 0; + } + if (!("GAP" in $$source)) { + this["GAP"] = (new GenericPerson()); + } + if (!("GPAs" in $$source)) { + this["GPAs"] = null; + } + if (!("GPAP" in $$source)) { + this["GPAP"] = null; + } + if (!("GMA" in $$source)) { + this["GMA"] = {}; + } + if (!("GPA" in $$source)) { + this["GPA"] = (new GenericPersonAlias()); + } + if (!("IPA" in $$source)) { + this["IPA"] = (new IndirectPersonAlias()); + } + if (!("TPIPA" in $$source)) { + this["TPIPA"] = (new TPIndirectPersonAlias()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new AliasGroup instance from a string or object. + */ + static createFrom($$source: any = {}): AliasGroup { + const $$createField1_0 = $$createType0; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType5; + const $$createField4_0 = $$createType6; + const $$createField5_0 = $$createType8; + const $$createField6_0 = $$createType8; + const $$createField7_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("GAP" in $$parsedSource) { + $$parsedSource["GAP"] = $$createField1_0($$parsedSource["GAP"]); + } + if ("GPAs" in $$parsedSource) { + $$parsedSource["GPAs"] = $$createField2_0($$parsedSource["GPAs"]); + } + if ("GPAP" in $$parsedSource) { + $$parsedSource["GPAP"] = $$createField3_0($$parsedSource["GPAP"]); + } + if ("GMA" in $$parsedSource) { + $$parsedSource["GMA"] = $$createField4_0($$parsedSource["GMA"]); + } + if ("GPA" in $$parsedSource) { + $$parsedSource["GPA"] = $$createField5_0($$parsedSource["GPA"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField6_0($$parsedSource["IPA"]); + } + if ("TPIPA" in $$parsedSource) { + $$parsedSource["TPIPA"] = $$createField7_0($$parsedSource["TPIPA"]); + } + return new AliasGroup($$parsedSource as Partial); + } +} + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + */ +export interface AliasStruct { + /** + * A field with a comment. + */ + "Foo": number[]; + + /** + * Definitely not Foo. + */ + "Bar"?: string; + "Baz"?: string; + + /** + * A nested alias struct. + */ + "Other": OtherAliasStruct; +} + +/** + * An empty struct alias. + */ +export interface EmptyAliasStruct { +} + +/** + * An empty struct. + */ +export class EmptyStruct { + + /** Creates a new EmptyStruct instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new EmptyStruct instance from a string or object. + */ + static createFrom($$source: any = {}): EmptyStruct { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new EmptyStruct($$parsedSource as Partial); + } +} + +/** + * A generic alias that forwards to a type parameter. + */ +export type GenericAlias = T; + +/** + * A generic alias that wraps a map. + */ +export type GenericMapAlias = { [_: string]: U }; + +/** + * A generic struct containing an alias. + */ +export class GenericPerson { + "Name"?: T; + "AliasedField": Alias; + + /** Creates a new GenericPerson instance. */ + constructor($$source: Partial> = {}) { + if (!("AliasedField" in $$source)) { + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class GenericPerson. + */ + static createFrom($$createParamT: (source: any) => T): ($$source?: any) => GenericPerson { + const $$createField0_0 = $$createParamT; + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Name" in $$parsedSource) { + $$parsedSource["Name"] = $$createField0_0($$parsedSource["Name"]); + } + return new GenericPerson($$parsedSource as Partial>); + }; + } +} + +/** + * A generic alias that wraps a generic struct. + */ +export const GenericPersonAlias = GenericPerson; + +/** + * A generic alias that wraps a generic struct. + */ +export type GenericPersonAlias = GenericPerson[]>; + +/** + * A generic alias that wraps a pointer type. + */ +export type GenericPtrAlias = GenericAlias | null; + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export const IndirectPersonAlias = GenericPersonAlias; + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export type IndirectPersonAlias = GenericPersonAlias; + +/** + * Another struct alias. + */ +export interface OtherAliasStruct { + "NoMoreIdeas": number[]; +} + +/** + * A non-generic struct containing an alias. + */ +export class Person { + /** + * The Person's name. + */ + "Name": string; + + /** + * A random alias field. + */ + "AliasedField": Alias; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("AliasedField" in $$source)) { + this["AliasedField"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person($$parsedSource as Partial); + } +} + +/** + * A class alias. + */ +export const AliasedPerson = Person; + +/** + * A class alias. + */ +export type AliasedPerson = Person; + +/** + * Another class alias, but ordered after its aliased class. + */ +export const StrangelyAliasedPerson = Person; + +/** + * Another class alias, but ordered after its aliased class. + */ +export type StrangelyAliasedPerson = Person; + +/** + * An alias that wraps a class through a typeparam alias. + */ +export const TPIndirectPersonAlias = GenericPerson; + +/** + * An alias that wraps a class through a typeparam alias. + */ +export type TPIndirectPersonAlias = GenericAlias>; + +// Private type creation functions +const $$createType0 = GenericPerson.createFrom($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = $Create.Nullable($$createType1); +const $$createType3 = $Create.Array($Create.Any); +const $$createType4 = GenericPerson.createFrom($$createType3); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Array($Create.Any); +const $$createType8 = GenericPerson.createFrom($$createType7); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts new file mode 100644 index 000000000..bdcf43c67 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts new file mode 100644 index 000000000..0780a0de3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function TestMethod(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service7.TestMethod"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts new file mode 100644 index 000000000..a2222265f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function TestMethod2(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service9.TestMethod2"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts new file mode 100644 index 000000000..f2106e7f4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(person: $models.Person, emb: $models.Embedded1): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts new file mode 100644 index 000000000..6cdc52c66 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts @@ -0,0 +1,17 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Embedded1, + Person, + Title +} from "./models.js"; + +export type { + Embedded3 +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts new file mode 100644 index 000000000..50f23b52d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts @@ -0,0 +1,237 @@ +// 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 "/wails/runtime.js"; + +export class Embedded1 { + /** + * Friends should be shadowed in Person by a field of lesser depth + */ + "Friends": number; + + /** + * Vanish should be omitted from Person because there is another field with same depth and no tag + */ + "Vanish": number; + + /** + * StillThere should be shadowed in Person by other field with same depth and a json tag + */ + "StillThere": string; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** Creates a new Embedded1 instance. */ + constructor($$source: Partial = {}) { + if (!("Friends" in $$source)) { + this["Friends"] = 0; + } + if (!("Vanish" in $$source)) { + this["Vanish"] = 0; + } + if (!("StillThere" in $$source)) { + this["StillThere"] = ""; + } + if (!("NamingThingsIsHard" in $$source)) { + this["NamingThingsIsHard"] = "false"; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Embedded1 instance from a string or object. + */ + static createFrom($$source: any = {}): Embedded1 { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Embedded1($$parsedSource as Partial); + } +} + +export type Embedded3 = string; + +/** + * Person represents a person + */ +export class Person { + /** + * Titles is optional in JSON + */ + "Titles"?: Title[]; + + /** + * Names has a + * multiline comment + */ + "Names": string[]; + + /** + * Partner has a custom and complex JSON key + */ + "Partner": Person | null; + "Friends": (Person | null)[]; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * StillThereButRenamed should shadow in Person the other field with same depth and no json tag + */ + "StillThere": Embedded3 | null; + + /** + * StrangeNumber maps to "-" + */ + "-": number; + + /** + * Embedded3 should appear with key "Embedded3" + */ + "Embedded3": Embedded3; + + /** + * StrangerNumber is serialized as a string + */ + "StrangerNumber": `${number}`; + + /** + * StrangestString is optional and serialized as a JSON string + */ + "StrangestString"?: `"${string}"`; + + /** + * StringStrangest is serialized as a JSON string and optional + */ + "StringStrangest"?: `"${string}"`; + + /** + * embedded4 should be optional and appear with key "emb4" + */ + "emb4"?: embedded4; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Names" in $$source)) { + this["Names"] = []; + } + if (!("Partner" in $$source)) { + this["Partner"] = null; + } + if (!("Friends" in $$source)) { + this["Friends"] = []; + } + if (!("NamingThingsIsHard" in $$source)) { + this["NamingThingsIsHard"] = "false"; + } + if (!("StillThere" in $$source)) { + this["StillThere"] = null; + } + if (!("-" in $$source)) { + this["-"] = 0; + } + if (!("Embedded3" in $$source)) { + this["Embedded3"] = ""; + } + if (!("StrangerNumber" in $$source)) { + this["StrangerNumber"] = "0"; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType3; + const $$createField3_0 = $$createType4; + const $$createField11_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Titles" in $$parsedSource) { + $$parsedSource["Titles"] = $$createField0_0($$parsedSource["Titles"]); + } + if ("Names" in $$parsedSource) { + $$parsedSource["Names"] = $$createField1_0($$parsedSource["Names"]); + } + if ("Partner" in $$parsedSource) { + $$parsedSource["Partner"] = $$createField2_0($$parsedSource["Partner"]); + } + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField3_0($$parsedSource["Friends"]); + } + if ("emb4" in $$parsedSource) { + $$parsedSource["emb4"] = $$createField11_0($$parsedSource["emb4"]); + } + return new Person($$parsedSource as Partial); + } +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; + +export class embedded4 { + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * Friends should not be shadowed in Person as embedded4 is not embedded + * from encoding/json's point of view; + * however, it should be shadowed in Embedded1 + */ + "Friends": boolean; + + /** Creates a new embedded4 instance. */ + constructor($$source: Partial = {}) { + if (!("NamingThingsIsHard" in $$source)) { + this["NamingThingsIsHard"] = "false"; + } + if (!("Friends" in $$source)) { + this["Friends"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new embedded4 instance from a string or object. + */ + static createFrom($$source: any = {}): embedded4 { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new embedded4($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Array($Create.Any); +const $$createType2 = Person.createFrom; +const $$createType3 = $Create.Nullable($$createType2); +const $$createType4 = $Create.Array($$createType3); +const $$createType5 = embedded4.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts new file mode 100644 index 000000000..4471270ed --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts @@ -0,0 +1,32 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + */ +export function Greet(str: string, people: $models.Person[], $2: {"AnotherCount": number, "AnotherOne": $models.Person | null}, assoc: { [_: `${number}`]: boolean | null }, $4: (number | null)[], ...other: string[]): $CancellablePromise<[$models.Person, any, number[]]> { + return $Call.ByName("main.GreetService.Greet", str, people, $2, assoc, $4, other).then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[2] = $$createType1($result[2]); + return $result; + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Array($Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts new file mode 100644 index 000000000..2417aff4c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts @@ -0,0 +1,30 @@ +// 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 "/wails/runtime.js"; + +/** + * Person represents a person + */ +export class Person { + "Name": string; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person($$parsedSource as Partial); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts new file mode 100644 index 000000000..2f0e45aff --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts @@ -0,0 +1,30 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.StructA, $models.StructC]> { + return $Call.ByName("main.GreetService.MakeCycles").then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + return $result; + }); +} + +// Private type creation functions +const $$createType0 = $models.StructA.createFrom; +const $$createType1 = $models.StructC.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts new file mode 100644 index 000000000..4b190b8da --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + StructA, + StructC, + StructE +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts new file mode 100644 index 000000000..9e86cd674 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts @@ -0,0 +1,131 @@ +// 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 "/wails/runtime.js"; + +export class StructA { + "B": structB | null; + + /** Creates a new StructA instance. */ + constructor($$source: Partial = {}) { + if (!("B" in $$source)) { + this["B"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructA instance from a string or object. + */ + static createFrom($$source: any = {}): StructA { + const $$createField0_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("B" in $$parsedSource) { + $$parsedSource["B"] = $$createField0_0($$parsedSource["B"]); + } + return new StructA($$parsedSource as Partial); + } +} + +export class StructC { + "D": structD; + + /** Creates a new StructC instance. */ + constructor($$source: Partial = {}) { + if (!("D" in $$source)) { + this["D"] = (new structD()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new StructC instance from a string or object. + */ + static createFrom($$source: any = {}): StructC { + const $$createField0_0 = $$createType2; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("D" in $$parsedSource) { + $$parsedSource["D"] = $$createField0_0($$parsedSource["D"]); + } + return new StructC($$parsedSource as Partial); + } +} + +export class StructE { + + /** Creates a new StructE instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new StructE instance from a string or object. + */ + static createFrom($$source: any = {}): StructE { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new StructE($$parsedSource as Partial); + } +} + +export class structB { + "A": StructA | null; + + /** Creates a new structB instance. */ + constructor($$source: Partial = {}) { + if (!("A" in $$source)) { + this["A"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structB instance from a string or object. + */ + static createFrom($$source: any = {}): structB { + const $$createField0_0 = $$createType4; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField0_0($$parsedSource["A"]); + } + return new structB($$parsedSource as Partial); + } +} + +export class structD { + "E": StructE; + + /** Creates a new structD instance. */ + constructor($$source: Partial = {}) { + if (!("E" in $$source)) { + this["E"] = (new StructE()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new structD instance from a string or object. + */ + static createFrom($$source: any = {}): structD { + const $$createField0_0 = $$createType5; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("E" in $$parsedSource) { + $$parsedSource["E"] = $$createField0_0($$parsedSource["E"]); + } + return new structD($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = structB.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = structD.createFrom; +const $$createType3 = StructA.createFrom; +const $$createType4 = $Create.Nullable($$createType3); +const $$createType5 = StructE.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts new file mode 100644 index 000000000..dba844168 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts @@ -0,0 +1,63 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]> { + return $Call.ByName("main.GreetService.MakeCycles").then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType9($result[1]); + return $result; + }); +} + +// Private type creation functions +var $$createType0 = (function $$initCreateType0(...args: any[]): any { + if ($$createType0 === $$initCreateType0) { + $$createType0 = $$createType3; + } + return $$createType0(...args); +}); +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = $Create.Map($Create.Any, $$createType1); +const $$createType3 = $Create.Array($$createType2); +var $$createType4 = (function $$initCreateType4(...args: any[]): any { + if ($$createType4 === $$initCreateType4) { + $$createType4 = $$createType8; + } + return $$createType4(...args); +}); +const $$createType5 = $Create.Nullable($$createType4); +const $$createType6 = $Create.Array($Create.Any); +const $$createType7 = $Create.Struct({ + "X": $$createType5, + "Y": $$createType6, +}); +const $$createType8 = $Create.Array($$createType7); +var $$createType9 = (function $$initCreateType9(...args: any[]): any { + if ($$createType9 === $$initCreateType9) { + $$createType9 = $$createType13; + } + return $$createType9(...args); +}); +const $$createType10 = $Create.Nullable($$createType9); +const $$createType11 = $Create.Array($$createType4); +const $$createType12 = $Create.Struct({ + "X": $$createType10, + "Y": $$createType11, +}); +const $$createType13 = $Create.Array($$createType12); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts new file mode 100644 index 000000000..16cef660c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Alias, + Cyclic, + GenericCyclic +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts new file mode 100644 index 000000000..93d1ef5c6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts @@ -0,0 +1,12 @@ +// 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 "/wails/runtime.js"; + +export type Alias = Cyclic | null; + +export type Cyclic = { [_: string]: Alias }[]; + +export type GenericCyclic = {"X": GenericCyclic | null, "Y": T[]}[]; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts new file mode 100644 index 000000000..b9fbdba96 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Classes!"); +console.log("Hello TS!"); +console.log("Hello TS Classes!"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts new file mode 100644 index 000000000..9271273bc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.InternalModel): $CancellablePromise { + return $Call.ByName("main.InternalService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts new file mode 100644 index 000000000..4d242fc2c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts @@ -0,0 +1,54 @@ +// 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 "/wails/runtime.js"; + +/** + * An exported but internal model. + */ +export class InternalModel { + "Field": string; + + /** Creates a new InternalModel instance. */ + constructor($$source: Partial = {}) { + if (!("Field" in $$source)) { + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new InternalModel instance from a string or object. + */ + static createFrom($$source: any = {}): InternalModel { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new InternalModel($$parsedSource as Partial); + } +} + +/** + * An unexported model. + */ +export class unexportedModel { + "Field": string; + + /** Creates a new unexportedModel instance. */ + constructor($$source: Partial = {}) { + if (!("Field" in $$source)) { + this["Field"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new unexportedModel instance from a string or object. + */ + static createFrom($$source: any = {}): unexportedModel { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new unexportedModel($$parsedSource as Partial); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts new file mode 100644 index 000000000..e52a0ccb8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Dummy +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts new file mode 100644 index 000000000..b927155d5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts @@ -0,0 +1,23 @@ +// 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 "/wails/runtime.js"; + +export class Dummy { + + /** Creates a new Dummy instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new Dummy instance from a string or object. + */ + static createFrom($$source: any = {}): Dummy { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Dummy($$parsedSource as Partial); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_t.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_t.ts new file mode 100644 index 000000000..6703820f1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_tc.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_tc.ts new file mode 100644 index 000000000..15d2994e9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/test_tc.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "../service.js"; + +CustomMethod("TS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts new file mode 100644 index 000000000..0687bd8ac --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts @@ -0,0 +1,22 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +function InternalMethod($0: string): $CancellablePromise { + return $Call.ByName("main.Service.InternalMethod", $0); +} + +export function VisibleMethod($0: otherpackage$0.Dummy): $CancellablePromise { + return $Call.ByName("main.Service.VisibleMethod", $0); +} + +export async function CustomMethod(arg: string): Promise { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js new file mode 100644 index 000000000..724e79e12 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_c.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Classes"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts new file mode 100644 index 000000000..253d3f2f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_tc.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_tc.ts new file mode 100644 index 000000000..66b739d3a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_tc.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS Classes"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts new file mode 100644 index 000000000..34b23e24d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.unexportedModel): $CancellablePromise { + return $Call.ByName("main.unexportedService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts new file mode 100644 index 000000000..c5b06be75 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts @@ -0,0 +1,47 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Comment 1. + */ +export function Method1(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method1"); +} + +/** + * Comment 2. + */ +export function Method2(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method2"); +} + +/** + * Comment 3a. + * Comment 3b. + */ +export function Method3(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method3"); +} + +/** + * Comment 4. + */ +export function Method4(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method4"); +} + +/** + * Comment 5. + */ +export function Method5(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method5"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts new file mode 100644 index 000000000..45abc153e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: $models.Title): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name, title); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts new file mode 100644 index 000000000..3b4d2f5c6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Person, + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts new file mode 100644 index 000000000..a50282a38 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts @@ -0,0 +1,82 @@ +// 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 "/wails/runtime.js"; + +/** + * Age is an integer with some predefined values + */ +export type Age = number; + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + */ +export class Person { + "Title": Title; + "Name": string; + "Age": Age; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Title" in $$source)) { + this["Title"] = Title.$zero; + } + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Age" in $$source)) { + this["Age"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Person($$parsedSource as Partial); + } +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts new file mode 100644 index 000000000..c4afd85da --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: services$0.Title): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts new file mode 100644 index 000000000..01c612edc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts new file mode 100644 index 000000000..661222bdf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts @@ -0,0 +1,22 @@ +// 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 "/wails/runtime.js"; + +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts new file mode 100644 index 000000000..1a2ffb266 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts new file mode 100644 index 000000000..f1ff262a1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts @@ -0,0 +1,46 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person + */ +export class Person { + "Name": string; + "Address": services$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts new file mode 100644 index 000000000..f44f172e7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services.OtherService.Yay").then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts new file mode 100644 index 000000000..1a2ffb266 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts new file mode 100644 index 000000000..88b2c78db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./services/other/models.js"; + +export class Person { + "Name": string; + "Address": other$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = other$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts new file mode 100644 index 000000000..fa9a93fc8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other.OtherService.Yay").then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts new file mode 100644 index 000000000..460b2b374 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts new file mode 100644 index 000000000..6a6a881dc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts new file mode 100644 index 000000000..aec011527 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts @@ -0,0 +1,25 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * Greet someone + */ +export function GreetWithContext(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.GreetWithContext", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts new file mode 100644 index 000000000..6a6a881dc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts new file mode 100644 index 000000000..b5890af28 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts @@ -0,0 +1,33 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Maps +} from "./models.js"; + +export type { + BasicCstrAlias, + ComparableCstrAlias, + EmbeddedCustomInterface, + EmbeddedOriginalInterface, + EmbeddedPointer, + EmbeddedPointerPtr, + EmbeddedValue, + EmbeddedValuePtr, + GoodTildeCstrAlias, + InterfaceCstrAlias, + MixedCstrAlias, + NonBasicCstrAlias, + PointableCstrAlias, + PointerAlias, + PointerTextMarshaler, + StringAlias, + StringType, + ValueAlias, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts new file mode 100644 index 000000000..0d26a6b0b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts @@ -0,0 +1,1886 @@ +// 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 "/wails/runtime.js"; + +export type BasicCstrAlias = S; + +export type ComparableCstrAlias = R; + +export type EmbeddedCustomInterface = string; + +export type EmbeddedOriginalInterface = string; + +export type EmbeddedPointer = string; + +export type EmbeddedPointerPtr = string; + +export type EmbeddedValue = string; + +export type EmbeddedValuePtr = string; + +export type GoodTildeCstrAlias = U; + +export type InterfaceCstrAlias = Y; + +export class Maps { + /** + * Reject + */ + "Bool": { [_: string]: number }; + + /** + * Accept + */ + "Int": { [_: `${number}`]: number }; + + /** + * Accept + */ + "Uint": { [_: `${number}`]: number }; + + /** + * Reject + */ + "Float": { [_: string]: number }; + + /** + * Reject + */ + "Complex": { [_: string]: number }; + + /** + * Accept + */ + "Byte": { [_: `${number}`]: number }; + + /** + * Accept + */ + "Rune": { [_: `${number}`]: number }; + + /** + * Accept + */ + "String": { [_: string]: number }; + + /** + * Reject + */ + "IntPtr": { [_: string]: number }; + + /** + * Reject + */ + "UintPtr": { [_: string]: number }; + + /** + * Reject + */ + "FloatPtr": { [_: string]: number }; + + /** + * Reject + */ + "ComplexPtr": { [_: string]: number }; + + /** + * Reject + */ + "StringPtr": { [_: string]: number }; + + /** + * Reject + */ + "NTM": { [_: string]: number }; + + /** + * Reject + */ + "NTMPtr": { [_: string]: number }; + + /** + * Accept + */ + "VTM": { [_: ValueTextMarshaler]: number }; + + /** + * Accept + */ + "VTMPtr": { [_: ValueTextMarshaler]: number }; + + /** + * Reject + */ + "PTM": { [_: string]: number }; + + /** + * Accept + */ + "PTMPtr": { [_: PointerTextMarshaler]: number }; + + /** + * Accept, hide + */ + "JTM": { [_: string]: number }; + + /** + * Accept, hide + */ + "JTMPtr": { [_: string]: number }; + + /** + * Reject + */ + "A": { [_: string]: number }; + + /** + * Reject + */ + "APtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TM": { [_: string]: number }; + + /** + * Reject + */ + "TMPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "CI": { [_: string]: number }; + + /** + * Reject + */ + "CIPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "EI": { [_: string]: number }; + + /** + * Reject + */ + "EIPtr": { [_: string]: number }; + + /** + * Accept + */ + "EV": { [_: EmbeddedValue]: number }; + + /** + * Accept + */ + "EVPtr": { [_: EmbeddedValue]: number }; + + /** + * Accept + */ + "EVP": { [_: EmbeddedValuePtr]: number }; + + /** + * Accept + */ + "EVPPtr": { [_: EmbeddedValuePtr]: number }; + + /** + * Reject + */ + "EP": { [_: string]: number }; + + /** + * Accept + */ + "EPPtr": { [_: EmbeddedPointer]: number }; + + /** + * Accept + */ + "EPP": { [_: EmbeddedPointerPtr]: number }; + + /** + * Accept + */ + "EPPPtr": { [_: EmbeddedPointerPtr]: number }; + + /** + * Accept + */ + "ECI": { [_: EmbeddedCustomInterface]: number }; + + /** + * Accept + */ + "ECIPtr": { [_: EmbeddedCustomInterface]: number }; + + /** + * Accept + */ + "EOI": { [_: EmbeddedOriginalInterface]: number }; + + /** + * Accept + */ + "EOIPtr": { [_: EmbeddedOriginalInterface]: number }; + + /** + * Reject + */ + "WT": { [_: string]: number }; + + /** + * Reject + */ + "WA": { [_: string]: number }; + + /** + * Accept + */ + "ST": { [_: StringType]: number }; + + /** + * Accept + */ + "SA": { [_: StringAlias]: number }; + + /** + * Accept + */ + "IntT": { [_: `${number}`]: number }; + + /** + * Accept + */ + "IntA": { [_: `${number}`]: number }; + + /** + * Reject + */ + "VT": { [_: string]: number }; + + /** + * Reject + */ + "VTPtr": { [_: string]: number }; + + /** + * Reject + */ + "VPT": { [_: string]: number }; + + /** + * Reject + */ + "VPTPtr": { [_: string]: number }; + + /** + * Accept + */ + "VA": { [_: ValueAlias]: number }; + + /** + * Accept + */ + "VAPtr": { [_: ValueAlias]: number }; + + /** + * Accept, hide + */ + "VPA": { [_: string]: number }; + + /** + * Reject + */ + "VPAPtr": { [_: string]: number }; + + /** + * Reject + */ + "PT": { [_: string]: number }; + + /** + * Reject + */ + "PTPtr": { [_: string]: number }; + + /** + * Reject + */ + "PPT": { [_: string]: number }; + + /** + * Reject + */ + "PPTPtr": { [_: string]: number }; + + /** + * Reject + */ + "PA": { [_: string]: number }; + + /** + * Accept + */ + "PAPtr": { [_: PointerAlias]: number }; + + /** + * Accept, hide + */ + "PPA": { [_: string]: number }; + + /** + * Reject + */ + "PPAPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "IT": { [_: string]: number }; + + /** + * Reject + */ + "ITPtr": { [_: string]: number }; + + /** + * Reject + */ + "IPT": { [_: string]: number }; + + /** + * Reject + */ + "IPTPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "IA": { [_: string]: number }; + + /** + * Reject + */ + "IAPtr": { [_: string]: number }; + + /** + * Reject + */ + "IPA": { [_: string]: number }; + + /** + * Reject + */ + "IPAPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "TPR": { [_: string]: number }; + + /** + * Soft reject + */ + "TPRPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPS": { [_: string]: number }; + + /** + * Soft reject + */ + "TPSPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "TPT": { [_: string]: number }; + + /** + * Soft reject + */ + "TPTPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPU": { [_: string]: number }; + + /** + * Soft reject + */ + "TPUPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPV": { [_: string]: number }; + + /** + * Soft reject + */ + "TPVPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "TPW": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPWPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPX": { [_: string]: number }; + + /** + * Soft reject + */ + "TPXPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPY": { [_: string]: number }; + + /** + * Soft reject + */ + "TPYPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "TPZ": { [_: string]: number }; + + /** + * Soft reject + */ + "TPZPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "GAR": { [_: string]: number }; + + /** + * Soft reject + */ + "GARPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAS": { [_: string]: number }; + + /** + * Soft reject + */ + "GASPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "GAT": { [_: string]: number }; + + /** + * Soft reject + */ + "GATPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAU": { [_: string]: number }; + + /** + * Soft reject + */ + "GAUPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAV": { [_: string]: number }; + + /** + * Soft reject + */ + "GAVPtr": { [_: string]: number }; + + /** + * Soft reject + */ + "GAW": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAWPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAX": { [_: string]: number }; + + /** + * Soft reject + */ + "GAXPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAY": { [_: string]: number }; + + /** + * Soft reject + */ + "GAYPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAZ": { [_: string]: number }; + + /** + * Soft reject + */ + "GAZPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GACi": { [_: `${number}`]: number }; + + /** + * Accept + */ + "GACV": { [_: ComparableCstrAlias]: number }; + + /** + * Reject + */ + "GACP": { [_: string]: number }; + + /** + * Reject + */ + "GACiPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GACVPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GACPPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GABi": { [_: `${number}`]: number }; + + /** + * Accept + */ + "GABs": { [_: BasicCstrAlias]: number }; + + /** + * Reject + */ + "GABiPtr": { [_: string]: number }; + + /** + * Reject + */ + "GABT": { [_: string]: number }; + + /** + * Reject + */ + "GABTPtr": { [_: string]: number }; + + /** + * Accept + */ + "GAGT": { [_: GoodTildeCstrAlias]: number }; + + /** + * Accept, hide + */ + "GAGTPtr": { [_: string]: number }; + + /** + * Accept + */ + "GANBV": { [_: NonBasicCstrAlias]: number }; + + /** + * Accept, hide + */ + "GANBP": { [_: string]: number }; + + /** + * Accept, hide + */ + "GANBVPtr": { [_: string]: number }; + + /** + * Reject + */ + "GANBPPtr": { [_: string]: number }; + + /** + * Accept + */ + "GAPlV1": { [_: PointableCstrAlias]: number }; + + /** + * Accept + */ + "GAPlV2": { [_: PointableCstrAlias]: number }; + + /** + * Reject + */ + "GAPlP1": { [_: string]: number }; + + /** + * Accept + */ + "GAPlP2": { [_: PointableCstrAlias]: number }; + + /** + * Accept, hide + */ + "GAPlVPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAPlPPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAMi": { [_: `${number}`]: number }; + + /** + * Accept + */ + "GAMS": { [_: MixedCstrAlias]: number }; + + /** + * Accept + */ + "GAMV": { [_: MixedCstrAlias]: number }; + + /** + * Reject + */ + "GAMSPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAMVPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAII": { [_: string]: number }; + + /** + * Accept + */ + "GAIV": { [_: InterfaceCstrAlias]: number }; + + /** + * Accept, hide + */ + "GAIP": { [_: string]: number }; + + /** + * Reject + */ + "GAIIPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAIVPtr": { [_: string]: number }; + + /** + * Reject + */ + "GAIPPtr": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAPrV": { [_: string]: number }; + + /** + * Accept, hide + */ + "GAPrP": { [_: string]: number }; + + /** + * Reject + */ + "GAPrVPtr": { [_: string]: number }; + + /** + * Reject + */ + "GAPrPPtr": { [_: string]: number }; + + /** Creates a new Maps instance. */ + constructor($$source: Partial> = {}) { + if (!("Bool" in $$source)) { + this["Bool"] = {}; + } + if (!("Int" in $$source)) { + this["Int"] = {}; + } + if (!("Uint" in $$source)) { + this["Uint"] = {}; + } + if (!("Float" in $$source)) { + this["Float"] = {}; + } + if (!("Complex" in $$source)) { + this["Complex"] = {}; + } + if (!("Byte" in $$source)) { + this["Byte"] = {}; + } + if (!("Rune" in $$source)) { + this["Rune"] = {}; + } + if (!("String" in $$source)) { + this["String"] = {}; + } + if (!("IntPtr" in $$source)) { + this["IntPtr"] = {}; + } + if (!("UintPtr" in $$source)) { + this["UintPtr"] = {}; + } + if (!("FloatPtr" in $$source)) { + this["FloatPtr"] = {}; + } + if (!("ComplexPtr" in $$source)) { + this["ComplexPtr"] = {}; + } + if (!("StringPtr" in $$source)) { + this["StringPtr"] = {}; + } + if (!("NTM" in $$source)) { + this["NTM"] = {}; + } + if (!("NTMPtr" in $$source)) { + this["NTMPtr"] = {}; + } + if (!("VTM" in $$source)) { + this["VTM"] = {}; + } + if (!("VTMPtr" in $$source)) { + this["VTMPtr"] = {}; + } + if (!("PTM" in $$source)) { + this["PTM"] = {}; + } + if (!("PTMPtr" in $$source)) { + this["PTMPtr"] = {}; + } + if (!("JTM" in $$source)) { + this["JTM"] = {}; + } + if (!("JTMPtr" in $$source)) { + this["JTMPtr"] = {}; + } + if (!("A" in $$source)) { + this["A"] = {}; + } + if (!("APtr" in $$source)) { + this["APtr"] = {}; + } + if (!("TM" in $$source)) { + this["TM"] = {}; + } + if (!("TMPtr" in $$source)) { + this["TMPtr"] = {}; + } + if (!("CI" in $$source)) { + this["CI"] = {}; + } + if (!("CIPtr" in $$source)) { + this["CIPtr"] = {}; + } + if (!("EI" in $$source)) { + this["EI"] = {}; + } + if (!("EIPtr" in $$source)) { + this["EIPtr"] = {}; + } + if (!("EV" in $$source)) { + this["EV"] = {}; + } + if (!("EVPtr" in $$source)) { + this["EVPtr"] = {}; + } + if (!("EVP" in $$source)) { + this["EVP"] = {}; + } + if (!("EVPPtr" in $$source)) { + this["EVPPtr"] = {}; + } + if (!("EP" in $$source)) { + this["EP"] = {}; + } + if (!("EPPtr" in $$source)) { + this["EPPtr"] = {}; + } + if (!("EPP" in $$source)) { + this["EPP"] = {}; + } + if (!("EPPPtr" in $$source)) { + this["EPPPtr"] = {}; + } + if (!("ECI" in $$source)) { + this["ECI"] = {}; + } + if (!("ECIPtr" in $$source)) { + this["ECIPtr"] = {}; + } + if (!("EOI" in $$source)) { + this["EOI"] = {}; + } + if (!("EOIPtr" in $$source)) { + this["EOIPtr"] = {}; + } + if (!("WT" in $$source)) { + this["WT"] = {}; + } + if (!("WA" in $$source)) { + this["WA"] = {}; + } + if (!("ST" in $$source)) { + this["ST"] = {}; + } + if (!("SA" in $$source)) { + this["SA"] = {}; + } + if (!("IntT" in $$source)) { + this["IntT"] = {}; + } + if (!("IntA" in $$source)) { + this["IntA"] = {}; + } + if (!("VT" in $$source)) { + this["VT"] = {}; + } + if (!("VTPtr" in $$source)) { + this["VTPtr"] = {}; + } + if (!("VPT" in $$source)) { + this["VPT"] = {}; + } + if (!("VPTPtr" in $$source)) { + this["VPTPtr"] = {}; + } + if (!("VA" in $$source)) { + this["VA"] = {}; + } + if (!("VAPtr" in $$source)) { + this["VAPtr"] = {}; + } + if (!("VPA" in $$source)) { + this["VPA"] = {}; + } + if (!("VPAPtr" in $$source)) { + this["VPAPtr"] = {}; + } + if (!("PT" in $$source)) { + this["PT"] = {}; + } + if (!("PTPtr" in $$source)) { + this["PTPtr"] = {}; + } + if (!("PPT" in $$source)) { + this["PPT"] = {}; + } + if (!("PPTPtr" in $$source)) { + this["PPTPtr"] = {}; + } + if (!("PA" in $$source)) { + this["PA"] = {}; + } + if (!("PAPtr" in $$source)) { + this["PAPtr"] = {}; + } + if (!("PPA" in $$source)) { + this["PPA"] = {}; + } + if (!("PPAPtr" in $$source)) { + this["PPAPtr"] = {}; + } + if (!("IT" in $$source)) { + this["IT"] = {}; + } + if (!("ITPtr" in $$source)) { + this["ITPtr"] = {}; + } + if (!("IPT" in $$source)) { + this["IPT"] = {}; + } + if (!("IPTPtr" in $$source)) { + this["IPTPtr"] = {}; + } + if (!("IA" in $$source)) { + this["IA"] = {}; + } + if (!("IAPtr" in $$source)) { + this["IAPtr"] = {}; + } + if (!("IPA" in $$source)) { + this["IPA"] = {}; + } + if (!("IPAPtr" in $$source)) { + this["IPAPtr"] = {}; + } + if (!("TPR" in $$source)) { + this["TPR"] = {}; + } + if (!("TPRPtr" in $$source)) { + this["TPRPtr"] = {}; + } + if (!("TPS" in $$source)) { + this["TPS"] = {}; + } + if (!("TPSPtr" in $$source)) { + this["TPSPtr"] = {}; + } + if (!("TPT" in $$source)) { + this["TPT"] = {}; + } + if (!("TPTPtr" in $$source)) { + this["TPTPtr"] = {}; + } + if (!("TPU" in $$source)) { + this["TPU"] = {}; + } + if (!("TPUPtr" in $$source)) { + this["TPUPtr"] = {}; + } + if (!("TPV" in $$source)) { + this["TPV"] = {}; + } + if (!("TPVPtr" in $$source)) { + this["TPVPtr"] = {}; + } + if (!("TPW" in $$source)) { + this["TPW"] = {}; + } + if (!("TPWPtr" in $$source)) { + this["TPWPtr"] = {}; + } + if (!("TPX" in $$source)) { + this["TPX"] = {}; + } + if (!("TPXPtr" in $$source)) { + this["TPXPtr"] = {}; + } + if (!("TPY" in $$source)) { + this["TPY"] = {}; + } + if (!("TPYPtr" in $$source)) { + this["TPYPtr"] = {}; + } + if (!("TPZ" in $$source)) { + this["TPZ"] = {}; + } + if (!("TPZPtr" in $$source)) { + this["TPZPtr"] = {}; + } + if (!("GAR" in $$source)) { + this["GAR"] = {}; + } + if (!("GARPtr" in $$source)) { + this["GARPtr"] = {}; + } + if (!("GAS" in $$source)) { + this["GAS"] = {}; + } + if (!("GASPtr" in $$source)) { + this["GASPtr"] = {}; + } + if (!("GAT" in $$source)) { + this["GAT"] = {}; + } + if (!("GATPtr" in $$source)) { + this["GATPtr"] = {}; + } + if (!("GAU" in $$source)) { + this["GAU"] = {}; + } + if (!("GAUPtr" in $$source)) { + this["GAUPtr"] = {}; + } + if (!("GAV" in $$source)) { + this["GAV"] = {}; + } + if (!("GAVPtr" in $$source)) { + this["GAVPtr"] = {}; + } + if (!("GAW" in $$source)) { + this["GAW"] = {}; + } + if (!("GAWPtr" in $$source)) { + this["GAWPtr"] = {}; + } + if (!("GAX" in $$source)) { + this["GAX"] = {}; + } + if (!("GAXPtr" in $$source)) { + this["GAXPtr"] = {}; + } + if (!("GAY" in $$source)) { + this["GAY"] = {}; + } + if (!("GAYPtr" in $$source)) { + this["GAYPtr"] = {}; + } + if (!("GAZ" in $$source)) { + this["GAZ"] = {}; + } + if (!("GAZPtr" in $$source)) { + this["GAZPtr"] = {}; + } + if (!("GACi" in $$source)) { + this["GACi"] = {}; + } + if (!("GACV" in $$source)) { + this["GACV"] = {}; + } + if (!("GACP" in $$source)) { + this["GACP"] = {}; + } + if (!("GACiPtr" in $$source)) { + this["GACiPtr"] = {}; + } + if (!("GACVPtr" in $$source)) { + this["GACVPtr"] = {}; + } + if (!("GACPPtr" in $$source)) { + this["GACPPtr"] = {}; + } + if (!("GABi" in $$source)) { + this["GABi"] = {}; + } + if (!("GABs" in $$source)) { + this["GABs"] = {}; + } + if (!("GABiPtr" in $$source)) { + this["GABiPtr"] = {}; + } + if (!("GABT" in $$source)) { + this["GABT"] = {}; + } + if (!("GABTPtr" in $$source)) { + this["GABTPtr"] = {}; + } + if (!("GAGT" in $$source)) { + this["GAGT"] = {}; + } + if (!("GAGTPtr" in $$source)) { + this["GAGTPtr"] = {}; + } + if (!("GANBV" in $$source)) { + this["GANBV"] = {}; + } + if (!("GANBP" in $$source)) { + this["GANBP"] = {}; + } + if (!("GANBVPtr" in $$source)) { + this["GANBVPtr"] = {}; + } + if (!("GANBPPtr" in $$source)) { + this["GANBPPtr"] = {}; + } + if (!("GAPlV1" in $$source)) { + this["GAPlV1"] = {}; + } + if (!("GAPlV2" in $$source)) { + this["GAPlV2"] = {}; + } + if (!("GAPlP1" in $$source)) { + this["GAPlP1"] = {}; + } + if (!("GAPlP2" in $$source)) { + this["GAPlP2"] = {}; + } + if (!("GAPlVPtr" in $$source)) { + this["GAPlVPtr"] = {}; + } + if (!("GAPlPPtr" in $$source)) { + this["GAPlPPtr"] = {}; + } + if (!("GAMi" in $$source)) { + this["GAMi"] = {}; + } + if (!("GAMS" in $$source)) { + this["GAMS"] = {}; + } + if (!("GAMV" in $$source)) { + this["GAMV"] = {}; + } + if (!("GAMSPtr" in $$source)) { + this["GAMSPtr"] = {}; + } + if (!("GAMVPtr" in $$source)) { + this["GAMVPtr"] = {}; + } + if (!("GAII" in $$source)) { + this["GAII"] = {}; + } + if (!("GAIV" in $$source)) { + this["GAIV"] = {}; + } + if (!("GAIP" in $$source)) { + this["GAIP"] = {}; + } + if (!("GAIIPtr" in $$source)) { + this["GAIIPtr"] = {}; + } + if (!("GAIVPtr" in $$source)) { + this["GAIVPtr"] = {}; + } + if (!("GAIPPtr" in $$source)) { + this["GAIPPtr"] = {}; + } + if (!("GAPrV" in $$source)) { + this["GAPrV"] = {}; + } + if (!("GAPrP" in $$source)) { + this["GAPrP"] = {}; + } + if (!("GAPrVPtr" in $$source)) { + this["GAPrVPtr"] = {}; + } + if (!("GAPrPPtr" in $$source)) { + this["GAPrPPtr"] = {}; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class Maps. + */ + static createFrom($$createParamR: (source: any) => R, $$createParamS: (source: any) => S, $$createParamT: (source: any) => T, $$createParamU: (source: any) => U, $$createParamV: (source: any) => V, $$createParamW: (source: any) => W, $$createParamX: (source: any) => X, $$createParamY: (source: any) => Y, $$createParamZ: (source: any) => Z): ($$source?: any) => Maps { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField2_0 = $$createType2; + const $$createField3_0 = $$createType3; + const $$createField4_0 = $$createType4; + const $$createField5_0 = $$createType5; + const $$createField6_0 = $$createType6; + const $$createField7_0 = $$createType7; + const $$createField8_0 = $$createType8; + const $$createField9_0 = $$createType9; + const $$createField10_0 = $$createType10; + const $$createField11_0 = $$createType11; + const $$createField12_0 = $$createType12; + const $$createField13_0 = $$createType13; + const $$createField14_0 = $$createType14; + const $$createField15_0 = $$createType15; + const $$createField16_0 = $$createType16; + const $$createField17_0 = $$createType17; + const $$createField18_0 = $$createType18; + const $$createField19_0 = $$createType19; + const $$createField20_0 = $$createType20; + const $$createField21_0 = $$createType21; + const $$createField22_0 = $$createType22; + const $$createField23_0 = $$createType23; + const $$createField24_0 = $$createType24; + const $$createField25_0 = $$createType25; + const $$createField26_0 = $$createType26; + const $$createField27_0 = $$createType27; + const $$createField28_0 = $$createType28; + const $$createField29_0 = $$createType29; + const $$createField30_0 = $$createType30; + const $$createField31_0 = $$createType31; + const $$createField32_0 = $$createType32; + const $$createField33_0 = $$createType33; + const $$createField34_0 = $$createType34; + const $$createField35_0 = $$createType35; + const $$createField36_0 = $$createType36; + const $$createField37_0 = $$createType37; + const $$createField38_0 = $$createType38; + const $$createField39_0 = $$createType39; + const $$createField40_0 = $$createType40; + const $$createField41_0 = $$createType41; + const $$createField42_0 = $$createType0; + const $$createField43_0 = $$createType42; + const $$createField44_0 = $$createType7; + const $$createField45_0 = $$createType43; + const $$createField46_0 = $$createType1; + const $$createField47_0 = $$createType44; + const $$createField48_0 = $$createType45; + const $$createField49_0 = $$createType46; + const $$createField50_0 = $$createType47; + const $$createField51_0 = $$createType15; + const $$createField52_0 = $$createType16; + const $$createField53_0 = $$createType16; + const $$createField54_0 = $$createType48; + const $$createField55_0 = $$createType49; + const $$createField56_0 = $$createType50; + const $$createField57_0 = $$createType51; + const $$createField58_0 = $$createType52; + const $$createField59_0 = $$createType17; + const $$createField60_0 = $$createType18; + const $$createField61_0 = $$createType18; + const $$createField62_0 = $$createType53; + const $$createField63_0 = $$createType54; + const $$createField64_0 = $$createType55; + const $$createField65_0 = $$createType56; + const $$createField66_0 = $$createType57; + const $$createField67_0 = $$createType23; + const $$createField68_0 = $$createType24; + const $$createField69_0 = $$createType24; + const $$createField70_0 = $$createType58; + const $$createField71_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField72_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField73_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField74_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField75_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField76_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField77_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField78_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField79_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField80_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField81_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField82_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField83_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField84_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField85_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField86_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField87_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField88_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField89_0 = $$createType59($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField90_0 = $$createType60($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField91_0 = $$createType61($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField92_0 = $$createType62($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField93_0 = $$createType63($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField94_0 = $$createType64($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField95_0 = $$createType65($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField96_0 = $$createType66($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField97_0 = $$createType67($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField98_0 = $$createType68($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField99_0 = $$createType69($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField100_0 = $$createType70($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField101_0 = $$createType71($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField102_0 = $$createType72($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField103_0 = $$createType73($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField104_0 = $$createType74($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField105_0 = $$createType75($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField106_0 = $$createType76($$createParamR, $$createParamS, $$createParamT, $$createParamU, $$createParamV, $$createParamW, $$createParamX, $$createParamY, $$createParamZ); + const $$createField107_0 = $$createType1; + const $$createField108_0 = $$createType15; + const $$createField109_0 = $$createType17; + const $$createField110_0 = $$createType8; + const $$createField111_0 = $$createType16; + const $$createField112_0 = $$createType18; + const $$createField113_0 = $$createType1; + const $$createField114_0 = $$createType7; + const $$createField115_0 = $$createType8; + const $$createField116_0 = $$createType77; + const $$createField117_0 = $$createType78; + const $$createField118_0 = $$createType15; + const $$createField119_0 = $$createType16; + const $$createField120_0 = $$createType15; + const $$createField121_0 = $$createType18; + const $$createField122_0 = $$createType16; + const $$createField123_0 = $$createType53; + const $$createField124_0 = $$createType15; + const $$createField125_0 = $$createType16; + const $$createField126_0 = $$createType17; + const $$createField127_0 = $$createType18; + const $$createField128_0 = $$createType16; + const $$createField129_0 = $$createType18; + const $$createField130_0 = $$createType2; + const $$createField131_0 = $$createType42; + const $$createField132_0 = $$createType15; + const $$createField133_0 = $$createType79; + const $$createField134_0 = $$createType16; + const $$createField135_0 = $$createType23; + const $$createField136_0 = $$createType15; + const $$createField137_0 = $$createType18; + const $$createField138_0 = $$createType24; + const $$createField139_0 = $$createType16; + const $$createField140_0 = $$createType53; + const $$createField141_0 = $$createType16; + const $$createField142_0 = $$createType18; + const $$createField143_0 = $$createType48; + const $$createField144_0 = $$createType53; + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Bool" in $$parsedSource) { + $$parsedSource["Bool"] = $$createField0_0($$parsedSource["Bool"]); + } + if ("Int" in $$parsedSource) { + $$parsedSource["Int"] = $$createField1_0($$parsedSource["Int"]); + } + if ("Uint" in $$parsedSource) { + $$parsedSource["Uint"] = $$createField2_0($$parsedSource["Uint"]); + } + if ("Float" in $$parsedSource) { + $$parsedSource["Float"] = $$createField3_0($$parsedSource["Float"]); + } + if ("Complex" in $$parsedSource) { + $$parsedSource["Complex"] = $$createField4_0($$parsedSource["Complex"]); + } + if ("Byte" in $$parsedSource) { + $$parsedSource["Byte"] = $$createField5_0($$parsedSource["Byte"]); + } + if ("Rune" in $$parsedSource) { + $$parsedSource["Rune"] = $$createField6_0($$parsedSource["Rune"]); + } + if ("String" in $$parsedSource) { + $$parsedSource["String"] = $$createField7_0($$parsedSource["String"]); + } + if ("IntPtr" in $$parsedSource) { + $$parsedSource["IntPtr"] = $$createField8_0($$parsedSource["IntPtr"]); + } + if ("UintPtr" in $$parsedSource) { + $$parsedSource["UintPtr"] = $$createField9_0($$parsedSource["UintPtr"]); + } + if ("FloatPtr" in $$parsedSource) { + $$parsedSource["FloatPtr"] = $$createField10_0($$parsedSource["FloatPtr"]); + } + if ("ComplexPtr" in $$parsedSource) { + $$parsedSource["ComplexPtr"] = $$createField11_0($$parsedSource["ComplexPtr"]); + } + if ("StringPtr" in $$parsedSource) { + $$parsedSource["StringPtr"] = $$createField12_0($$parsedSource["StringPtr"]); + } + if ("NTM" in $$parsedSource) { + $$parsedSource["NTM"] = $$createField13_0($$parsedSource["NTM"]); + } + if ("NTMPtr" in $$parsedSource) { + $$parsedSource["NTMPtr"] = $$createField14_0($$parsedSource["NTMPtr"]); + } + if ("VTM" in $$parsedSource) { + $$parsedSource["VTM"] = $$createField15_0($$parsedSource["VTM"]); + } + if ("VTMPtr" in $$parsedSource) { + $$parsedSource["VTMPtr"] = $$createField16_0($$parsedSource["VTMPtr"]); + } + if ("PTM" in $$parsedSource) { + $$parsedSource["PTM"] = $$createField17_0($$parsedSource["PTM"]); + } + if ("PTMPtr" in $$parsedSource) { + $$parsedSource["PTMPtr"] = $$createField18_0($$parsedSource["PTMPtr"]); + } + if ("JTM" in $$parsedSource) { + $$parsedSource["JTM"] = $$createField19_0($$parsedSource["JTM"]); + } + if ("JTMPtr" in $$parsedSource) { + $$parsedSource["JTMPtr"] = $$createField20_0($$parsedSource["JTMPtr"]); + } + if ("A" in $$parsedSource) { + $$parsedSource["A"] = $$createField21_0($$parsedSource["A"]); + } + if ("APtr" in $$parsedSource) { + $$parsedSource["APtr"] = $$createField22_0($$parsedSource["APtr"]); + } + if ("TM" in $$parsedSource) { + $$parsedSource["TM"] = $$createField23_0($$parsedSource["TM"]); + } + if ("TMPtr" in $$parsedSource) { + $$parsedSource["TMPtr"] = $$createField24_0($$parsedSource["TMPtr"]); + } + if ("CI" in $$parsedSource) { + $$parsedSource["CI"] = $$createField25_0($$parsedSource["CI"]); + } + if ("CIPtr" in $$parsedSource) { + $$parsedSource["CIPtr"] = $$createField26_0($$parsedSource["CIPtr"]); + } + if ("EI" in $$parsedSource) { + $$parsedSource["EI"] = $$createField27_0($$parsedSource["EI"]); + } + if ("EIPtr" in $$parsedSource) { + $$parsedSource["EIPtr"] = $$createField28_0($$parsedSource["EIPtr"]); + } + if ("EV" in $$parsedSource) { + $$parsedSource["EV"] = $$createField29_0($$parsedSource["EV"]); + } + if ("EVPtr" in $$parsedSource) { + $$parsedSource["EVPtr"] = $$createField30_0($$parsedSource["EVPtr"]); + } + if ("EVP" in $$parsedSource) { + $$parsedSource["EVP"] = $$createField31_0($$parsedSource["EVP"]); + } + if ("EVPPtr" in $$parsedSource) { + $$parsedSource["EVPPtr"] = $$createField32_0($$parsedSource["EVPPtr"]); + } + if ("EP" in $$parsedSource) { + $$parsedSource["EP"] = $$createField33_0($$parsedSource["EP"]); + } + if ("EPPtr" in $$parsedSource) { + $$parsedSource["EPPtr"] = $$createField34_0($$parsedSource["EPPtr"]); + } + if ("EPP" in $$parsedSource) { + $$parsedSource["EPP"] = $$createField35_0($$parsedSource["EPP"]); + } + if ("EPPPtr" in $$parsedSource) { + $$parsedSource["EPPPtr"] = $$createField36_0($$parsedSource["EPPPtr"]); + } + if ("ECI" in $$parsedSource) { + $$parsedSource["ECI"] = $$createField37_0($$parsedSource["ECI"]); + } + if ("ECIPtr" in $$parsedSource) { + $$parsedSource["ECIPtr"] = $$createField38_0($$parsedSource["ECIPtr"]); + } + if ("EOI" in $$parsedSource) { + $$parsedSource["EOI"] = $$createField39_0($$parsedSource["EOI"]); + } + if ("EOIPtr" in $$parsedSource) { + $$parsedSource["EOIPtr"] = $$createField40_0($$parsedSource["EOIPtr"]); + } + if ("WT" in $$parsedSource) { + $$parsedSource["WT"] = $$createField41_0($$parsedSource["WT"]); + } + if ("WA" in $$parsedSource) { + $$parsedSource["WA"] = $$createField42_0($$parsedSource["WA"]); + } + if ("ST" in $$parsedSource) { + $$parsedSource["ST"] = $$createField43_0($$parsedSource["ST"]); + } + if ("SA" in $$parsedSource) { + $$parsedSource["SA"] = $$createField44_0($$parsedSource["SA"]); + } + if ("IntT" in $$parsedSource) { + $$parsedSource["IntT"] = $$createField45_0($$parsedSource["IntT"]); + } + if ("IntA" in $$parsedSource) { + $$parsedSource["IntA"] = $$createField46_0($$parsedSource["IntA"]); + } + if ("VT" in $$parsedSource) { + $$parsedSource["VT"] = $$createField47_0($$parsedSource["VT"]); + } + if ("VTPtr" in $$parsedSource) { + $$parsedSource["VTPtr"] = $$createField48_0($$parsedSource["VTPtr"]); + } + if ("VPT" in $$parsedSource) { + $$parsedSource["VPT"] = $$createField49_0($$parsedSource["VPT"]); + } + if ("VPTPtr" in $$parsedSource) { + $$parsedSource["VPTPtr"] = $$createField50_0($$parsedSource["VPTPtr"]); + } + if ("VA" in $$parsedSource) { + $$parsedSource["VA"] = $$createField51_0($$parsedSource["VA"]); + } + if ("VAPtr" in $$parsedSource) { + $$parsedSource["VAPtr"] = $$createField52_0($$parsedSource["VAPtr"]); + } + if ("VPA" in $$parsedSource) { + $$parsedSource["VPA"] = $$createField53_0($$parsedSource["VPA"]); + } + if ("VPAPtr" in $$parsedSource) { + $$parsedSource["VPAPtr"] = $$createField54_0($$parsedSource["VPAPtr"]); + } + if ("PT" in $$parsedSource) { + $$parsedSource["PT"] = $$createField55_0($$parsedSource["PT"]); + } + if ("PTPtr" in $$parsedSource) { + $$parsedSource["PTPtr"] = $$createField56_0($$parsedSource["PTPtr"]); + } + if ("PPT" in $$parsedSource) { + $$parsedSource["PPT"] = $$createField57_0($$parsedSource["PPT"]); + } + if ("PPTPtr" in $$parsedSource) { + $$parsedSource["PPTPtr"] = $$createField58_0($$parsedSource["PPTPtr"]); + } + if ("PA" in $$parsedSource) { + $$parsedSource["PA"] = $$createField59_0($$parsedSource["PA"]); + } + if ("PAPtr" in $$parsedSource) { + $$parsedSource["PAPtr"] = $$createField60_0($$parsedSource["PAPtr"]); + } + if ("PPA" in $$parsedSource) { + $$parsedSource["PPA"] = $$createField61_0($$parsedSource["PPA"]); + } + if ("PPAPtr" in $$parsedSource) { + $$parsedSource["PPAPtr"] = $$createField62_0($$parsedSource["PPAPtr"]); + } + if ("IT" in $$parsedSource) { + $$parsedSource["IT"] = $$createField63_0($$parsedSource["IT"]); + } + if ("ITPtr" in $$parsedSource) { + $$parsedSource["ITPtr"] = $$createField64_0($$parsedSource["ITPtr"]); + } + if ("IPT" in $$parsedSource) { + $$parsedSource["IPT"] = $$createField65_0($$parsedSource["IPT"]); + } + if ("IPTPtr" in $$parsedSource) { + $$parsedSource["IPTPtr"] = $$createField66_0($$parsedSource["IPTPtr"]); + } + if ("IA" in $$parsedSource) { + $$parsedSource["IA"] = $$createField67_0($$parsedSource["IA"]); + } + if ("IAPtr" in $$parsedSource) { + $$parsedSource["IAPtr"] = $$createField68_0($$parsedSource["IAPtr"]); + } + if ("IPA" in $$parsedSource) { + $$parsedSource["IPA"] = $$createField69_0($$parsedSource["IPA"]); + } + if ("IPAPtr" in $$parsedSource) { + $$parsedSource["IPAPtr"] = $$createField70_0($$parsedSource["IPAPtr"]); + } + if ("TPR" in $$parsedSource) { + $$parsedSource["TPR"] = $$createField71_0($$parsedSource["TPR"]); + } + if ("TPRPtr" in $$parsedSource) { + $$parsedSource["TPRPtr"] = $$createField72_0($$parsedSource["TPRPtr"]); + } + if ("TPS" in $$parsedSource) { + $$parsedSource["TPS"] = $$createField73_0($$parsedSource["TPS"]); + } + if ("TPSPtr" in $$parsedSource) { + $$parsedSource["TPSPtr"] = $$createField74_0($$parsedSource["TPSPtr"]); + } + if ("TPT" in $$parsedSource) { + $$parsedSource["TPT"] = $$createField75_0($$parsedSource["TPT"]); + } + if ("TPTPtr" in $$parsedSource) { + $$parsedSource["TPTPtr"] = $$createField76_0($$parsedSource["TPTPtr"]); + } + if ("TPU" in $$parsedSource) { + $$parsedSource["TPU"] = $$createField77_0($$parsedSource["TPU"]); + } + if ("TPUPtr" in $$parsedSource) { + $$parsedSource["TPUPtr"] = $$createField78_0($$parsedSource["TPUPtr"]); + } + if ("TPV" in $$parsedSource) { + $$parsedSource["TPV"] = $$createField79_0($$parsedSource["TPV"]); + } + if ("TPVPtr" in $$parsedSource) { + $$parsedSource["TPVPtr"] = $$createField80_0($$parsedSource["TPVPtr"]); + } + if ("TPW" in $$parsedSource) { + $$parsedSource["TPW"] = $$createField81_0($$parsedSource["TPW"]); + } + if ("TPWPtr" in $$parsedSource) { + $$parsedSource["TPWPtr"] = $$createField82_0($$parsedSource["TPWPtr"]); + } + if ("TPX" in $$parsedSource) { + $$parsedSource["TPX"] = $$createField83_0($$parsedSource["TPX"]); + } + if ("TPXPtr" in $$parsedSource) { + $$parsedSource["TPXPtr"] = $$createField84_0($$parsedSource["TPXPtr"]); + } + if ("TPY" in $$parsedSource) { + $$parsedSource["TPY"] = $$createField85_0($$parsedSource["TPY"]); + } + if ("TPYPtr" in $$parsedSource) { + $$parsedSource["TPYPtr"] = $$createField86_0($$parsedSource["TPYPtr"]); + } + if ("TPZ" in $$parsedSource) { + $$parsedSource["TPZ"] = $$createField87_0($$parsedSource["TPZ"]); + } + if ("TPZPtr" in $$parsedSource) { + $$parsedSource["TPZPtr"] = $$createField88_0($$parsedSource["TPZPtr"]); + } + if ("GAR" in $$parsedSource) { + $$parsedSource["GAR"] = $$createField89_0($$parsedSource["GAR"]); + } + if ("GARPtr" in $$parsedSource) { + $$parsedSource["GARPtr"] = $$createField90_0($$parsedSource["GARPtr"]); + } + if ("GAS" in $$parsedSource) { + $$parsedSource["GAS"] = $$createField91_0($$parsedSource["GAS"]); + } + if ("GASPtr" in $$parsedSource) { + $$parsedSource["GASPtr"] = $$createField92_0($$parsedSource["GASPtr"]); + } + if ("GAT" in $$parsedSource) { + $$parsedSource["GAT"] = $$createField93_0($$parsedSource["GAT"]); + } + if ("GATPtr" in $$parsedSource) { + $$parsedSource["GATPtr"] = $$createField94_0($$parsedSource["GATPtr"]); + } + if ("GAU" in $$parsedSource) { + $$parsedSource["GAU"] = $$createField95_0($$parsedSource["GAU"]); + } + if ("GAUPtr" in $$parsedSource) { + $$parsedSource["GAUPtr"] = $$createField96_0($$parsedSource["GAUPtr"]); + } + if ("GAV" in $$parsedSource) { + $$parsedSource["GAV"] = $$createField97_0($$parsedSource["GAV"]); + } + if ("GAVPtr" in $$parsedSource) { + $$parsedSource["GAVPtr"] = $$createField98_0($$parsedSource["GAVPtr"]); + } + if ("GAW" in $$parsedSource) { + $$parsedSource["GAW"] = $$createField99_0($$parsedSource["GAW"]); + } + if ("GAWPtr" in $$parsedSource) { + $$parsedSource["GAWPtr"] = $$createField100_0($$parsedSource["GAWPtr"]); + } + if ("GAX" in $$parsedSource) { + $$parsedSource["GAX"] = $$createField101_0($$parsedSource["GAX"]); + } + if ("GAXPtr" in $$parsedSource) { + $$parsedSource["GAXPtr"] = $$createField102_0($$parsedSource["GAXPtr"]); + } + if ("GAY" in $$parsedSource) { + $$parsedSource["GAY"] = $$createField103_0($$parsedSource["GAY"]); + } + if ("GAYPtr" in $$parsedSource) { + $$parsedSource["GAYPtr"] = $$createField104_0($$parsedSource["GAYPtr"]); + } + if ("GAZ" in $$parsedSource) { + $$parsedSource["GAZ"] = $$createField105_0($$parsedSource["GAZ"]); + } + if ("GAZPtr" in $$parsedSource) { + $$parsedSource["GAZPtr"] = $$createField106_0($$parsedSource["GAZPtr"]); + } + if ("GACi" in $$parsedSource) { + $$parsedSource["GACi"] = $$createField107_0($$parsedSource["GACi"]); + } + if ("GACV" in $$parsedSource) { + $$parsedSource["GACV"] = $$createField108_0($$parsedSource["GACV"]); + } + if ("GACP" in $$parsedSource) { + $$parsedSource["GACP"] = $$createField109_0($$parsedSource["GACP"]); + } + if ("GACiPtr" in $$parsedSource) { + $$parsedSource["GACiPtr"] = $$createField110_0($$parsedSource["GACiPtr"]); + } + if ("GACVPtr" in $$parsedSource) { + $$parsedSource["GACVPtr"] = $$createField111_0($$parsedSource["GACVPtr"]); + } + if ("GACPPtr" in $$parsedSource) { + $$parsedSource["GACPPtr"] = $$createField112_0($$parsedSource["GACPPtr"]); + } + if ("GABi" in $$parsedSource) { + $$parsedSource["GABi"] = $$createField113_0($$parsedSource["GABi"]); + } + if ("GABs" in $$parsedSource) { + $$parsedSource["GABs"] = $$createField114_0($$parsedSource["GABs"]); + } + if ("GABiPtr" in $$parsedSource) { + $$parsedSource["GABiPtr"] = $$createField115_0($$parsedSource["GABiPtr"]); + } + if ("GABT" in $$parsedSource) { + $$parsedSource["GABT"] = $$createField116_0($$parsedSource["GABT"]); + } + if ("GABTPtr" in $$parsedSource) { + $$parsedSource["GABTPtr"] = $$createField117_0($$parsedSource["GABTPtr"]); + } + if ("GAGT" in $$parsedSource) { + $$parsedSource["GAGT"] = $$createField118_0($$parsedSource["GAGT"]); + } + if ("GAGTPtr" in $$parsedSource) { + $$parsedSource["GAGTPtr"] = $$createField119_0($$parsedSource["GAGTPtr"]); + } + if ("GANBV" in $$parsedSource) { + $$parsedSource["GANBV"] = $$createField120_0($$parsedSource["GANBV"]); + } + if ("GANBP" in $$parsedSource) { + $$parsedSource["GANBP"] = $$createField121_0($$parsedSource["GANBP"]); + } + if ("GANBVPtr" in $$parsedSource) { + $$parsedSource["GANBVPtr"] = $$createField122_0($$parsedSource["GANBVPtr"]); + } + if ("GANBPPtr" in $$parsedSource) { + $$parsedSource["GANBPPtr"] = $$createField123_0($$parsedSource["GANBPPtr"]); + } + if ("GAPlV1" in $$parsedSource) { + $$parsedSource["GAPlV1"] = $$createField124_0($$parsedSource["GAPlV1"]); + } + if ("GAPlV2" in $$parsedSource) { + $$parsedSource["GAPlV2"] = $$createField125_0($$parsedSource["GAPlV2"]); + } + if ("GAPlP1" in $$parsedSource) { + $$parsedSource["GAPlP1"] = $$createField126_0($$parsedSource["GAPlP1"]); + } + if ("GAPlP2" in $$parsedSource) { + $$parsedSource["GAPlP2"] = $$createField127_0($$parsedSource["GAPlP2"]); + } + if ("GAPlVPtr" in $$parsedSource) { + $$parsedSource["GAPlVPtr"] = $$createField128_0($$parsedSource["GAPlVPtr"]); + } + if ("GAPlPPtr" in $$parsedSource) { + $$parsedSource["GAPlPPtr"] = $$createField129_0($$parsedSource["GAPlPPtr"]); + } + if ("GAMi" in $$parsedSource) { + $$parsedSource["GAMi"] = $$createField130_0($$parsedSource["GAMi"]); + } + if ("GAMS" in $$parsedSource) { + $$parsedSource["GAMS"] = $$createField131_0($$parsedSource["GAMS"]); + } + if ("GAMV" in $$parsedSource) { + $$parsedSource["GAMV"] = $$createField132_0($$parsedSource["GAMV"]); + } + if ("GAMSPtr" in $$parsedSource) { + $$parsedSource["GAMSPtr"] = $$createField133_0($$parsedSource["GAMSPtr"]); + } + if ("GAMVPtr" in $$parsedSource) { + $$parsedSource["GAMVPtr"] = $$createField134_0($$parsedSource["GAMVPtr"]); + } + if ("GAII" in $$parsedSource) { + $$parsedSource["GAII"] = $$createField135_0($$parsedSource["GAII"]); + } + if ("GAIV" in $$parsedSource) { + $$parsedSource["GAIV"] = $$createField136_0($$parsedSource["GAIV"]); + } + if ("GAIP" in $$parsedSource) { + $$parsedSource["GAIP"] = $$createField137_0($$parsedSource["GAIP"]); + } + if ("GAIIPtr" in $$parsedSource) { + $$parsedSource["GAIIPtr"] = $$createField138_0($$parsedSource["GAIIPtr"]); + } + if ("GAIVPtr" in $$parsedSource) { + $$parsedSource["GAIVPtr"] = $$createField139_0($$parsedSource["GAIVPtr"]); + } + if ("GAIPPtr" in $$parsedSource) { + $$parsedSource["GAIPPtr"] = $$createField140_0($$parsedSource["GAIPPtr"]); + } + if ("GAPrV" in $$parsedSource) { + $$parsedSource["GAPrV"] = $$createField141_0($$parsedSource["GAPrV"]); + } + if ("GAPrP" in $$parsedSource) { + $$parsedSource["GAPrP"] = $$createField142_0($$parsedSource["GAPrP"]); + } + if ("GAPrVPtr" in $$parsedSource) { + $$parsedSource["GAPrVPtr"] = $$createField143_0($$parsedSource["GAPrVPtr"]); + } + if ("GAPrPPtr" in $$parsedSource) { + $$parsedSource["GAPrPPtr"] = $$createField144_0($$parsedSource["GAPrPPtr"]); + } + return new Maps($$parsedSource as Partial>); + }; + } +} + +export type MixedCstrAlias = X; + +export type NonBasicCstrAlias = V; + +export type PointableCstrAlias = W; + +export type PointerAlias = PointerTextMarshaler; + +export type PointerTextMarshaler = string; + +export type StringAlias = string; + +export type StringType = string; + +export type ValueAlias = ValueTextMarshaler; + +export type ValueTextMarshaler = string; + +// Private type creation functions +const $$createType0 = $Create.Map($Create.Any, $Create.Any); +const $$createType1 = $Create.Map($Create.Any, $Create.Any); +const $$createType2 = $Create.Map($Create.Any, $Create.Any); +const $$createType3 = $Create.Map($Create.Any, $Create.Any); +const $$createType4 = $Create.Map($Create.Any, $Create.Any); +const $$createType5 = $Create.Map($Create.Any, $Create.Any); +const $$createType6 = $Create.Map($Create.Any, $Create.Any); +const $$createType7 = $Create.Map($Create.Any, $Create.Any); +const $$createType8 = $Create.Map($Create.Any, $Create.Any); +const $$createType9 = $Create.Map($Create.Any, $Create.Any); +const $$createType10 = $Create.Map($Create.Any, $Create.Any); +const $$createType11 = $Create.Map($Create.Any, $Create.Any); +const $$createType12 = $Create.Map($Create.Any, $Create.Any); +const $$createType13 = $Create.Map($Create.Any, $Create.Any); +const $$createType14 = $Create.Map($Create.Any, $Create.Any); +const $$createType15 = $Create.Map($Create.Any, $Create.Any); +const $$createType16 = $Create.Map($Create.Any, $Create.Any); +const $$createType17 = $Create.Map($Create.Any, $Create.Any); +const $$createType18 = $Create.Map($Create.Any, $Create.Any); +const $$createType19 = $Create.Map($Create.Any, $Create.Any); +const $$createType20 = $Create.Map($Create.Any, $Create.Any); +const $$createType21 = $Create.Map($Create.Any, $Create.Any); +const $$createType22 = $Create.Map($Create.Any, $Create.Any); +const $$createType23 = $Create.Map($Create.Any, $Create.Any); +const $$createType24 = $Create.Map($Create.Any, $Create.Any); +const $$createType25 = $Create.Map($Create.Any, $Create.Any); +const $$createType26 = $Create.Map($Create.Any, $Create.Any); +const $$createType27 = $Create.Map($Create.Any, $Create.Any); +const $$createType28 = $Create.Map($Create.Any, $Create.Any); +const $$createType29 = $Create.Map($Create.Any, $Create.Any); +const $$createType30 = $Create.Map($Create.Any, $Create.Any); +const $$createType31 = $Create.Map($Create.Any, $Create.Any); +const $$createType32 = $Create.Map($Create.Any, $Create.Any); +const $$createType33 = $Create.Map($Create.Any, $Create.Any); +const $$createType34 = $Create.Map($Create.Any, $Create.Any); +const $$createType35 = $Create.Map($Create.Any, $Create.Any); +const $$createType36 = $Create.Map($Create.Any, $Create.Any); +const $$createType37 = $Create.Map($Create.Any, $Create.Any); +const $$createType38 = $Create.Map($Create.Any, $Create.Any); +const $$createType39 = $Create.Map($Create.Any, $Create.Any); +const $$createType40 = $Create.Map($Create.Any, $Create.Any); +const $$createType41 = $Create.Map($Create.Any, $Create.Any); +const $$createType42 = $Create.Map($Create.Any, $Create.Any); +const $$createType43 = $Create.Map($Create.Any, $Create.Any); +const $$createType44 = $Create.Map($Create.Any, $Create.Any); +const $$createType45 = $Create.Map($Create.Any, $Create.Any); +const $$createType46 = $Create.Map($Create.Any, $Create.Any); +const $$createType47 = $Create.Map($Create.Any, $Create.Any); +const $$createType48 = $Create.Map($Create.Any, $Create.Any); +const $$createType49 = $Create.Map($Create.Any, $Create.Any); +const $$createType50 = $Create.Map($Create.Any, $Create.Any); +const $$createType51 = $Create.Map($Create.Any, $Create.Any); +const $$createType52 = $Create.Map($Create.Any, $Create.Any); +const $$createType53 = $Create.Map($Create.Any, $Create.Any); +const $$createType54 = $Create.Map($Create.Any, $Create.Any); +const $$createType55 = $Create.Map($Create.Any, $Create.Any); +const $$createType56 = $Create.Map($Create.Any, $Create.Any); +const $$createType57 = $Create.Map($Create.Any, $Create.Any); +const $$createType58 = $Create.Map($Create.Any, $Create.Any); +const $$createType59 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType60 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType61 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType62 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType63 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType64 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType65 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType66 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType67 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType68 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType69 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType70 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType71 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType72 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType73 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType74 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType75 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType76 = ($$createParamR: any, $$createParamS: any, $$createParamT: any, $$createParamU: any, $$createParamV: any, $$createParamW: any, $$createParamX: any, $$createParamY: any, $$createParamZ: any) => $Create.Map($Create.Any, $Create.Any); +const $$createType77 = $Create.Map($Create.Any, $Create.Any); +const $$createType78 = $Create.Map($Create.Any, $Create.Any); +const $$createType79 = $Create.Map($Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts new file mode 100644 index 000000000..d62acda96 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts @@ -0,0 +1,19 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>> { + return $Call.ByName("main.Service.Method").then(($result: any) => { + return $$createType0($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Maps.createFrom($Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any, $Create.Any); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts new file mode 100644 index 000000000..bbe49e32f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts @@ -0,0 +1,36 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Data, + ImplicitNonMarshaler, + NonMarshaler +} from "./models.js"; + +export type { + AliasJsonMarshaler, + AliasMarshaler, + AliasNonMarshaler, + AliasTextMarshaler, + ImplicitJsonButText, + ImplicitJsonMarshaler, + ImplicitMarshaler, + ImplicitNonJson, + ImplicitNonText, + ImplicitTextButJson, + ImplicitTextMarshaler, + PointerJsonMarshaler, + PointerMarshaler, + PointerTextMarshaler, + UnderlyingJsonMarshaler, + UnderlyingMarshaler, + UnderlyingTextMarshaler, + ValueJsonMarshaler, + ValueMarshaler, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts new file mode 100644 index 000000000..d8db23862 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts @@ -0,0 +1,545 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + */ +export type AliasJsonMarshaler = any; + +/** + * any + */ +export type AliasMarshaler = any; + +/** + * struct{} + */ +export interface AliasNonMarshaler { +} + +/** + * string + */ +export type AliasTextMarshaler = string; + +export class Data { + "NM": NonMarshaler; + + /** + * NonMarshaler | null + */ + "NMPtr": NonMarshaler | null; + "VJM": ValueJsonMarshaler; + + /** + * ValueJsonMarshaler | null + */ + "VJMPtr": ValueJsonMarshaler | null; + "PJM": PointerJsonMarshaler; + + /** + * PointerJsonMarshaler | null + */ + "PJMPtr": PointerJsonMarshaler | null; + "VTM": ValueTextMarshaler; + + /** + * ValueTextMarshaler | null + */ + "VTMPtr": ValueTextMarshaler | null; + "PTM": PointerTextMarshaler; + + /** + * PointerTextMarshaler | null + */ + "PTMPtr": PointerTextMarshaler | null; + "VM": ValueMarshaler; + + /** + * ValueMarshaler | null + */ + "VMPtr": ValueMarshaler | null; + "PM": PointerMarshaler; + + /** + * PointerMarshaler | null + */ + "PMPtr": PointerMarshaler | null; + "UJM": UnderlyingJsonMarshaler; + + /** + * UnderlyingJsonMarshaler | null + */ + "UJMPtr": UnderlyingJsonMarshaler | null; + "UTM": UnderlyingTextMarshaler; + + /** + * UnderlyingTextMarshaler | null + */ + "UTMPtr": UnderlyingTextMarshaler | null; + "UM": UnderlyingMarshaler; + + /** + * UnderlyingMarshaler | null + */ + "UMPtr": UnderlyingMarshaler | null; + + /** + * any + */ + "JM": any; + + /** + * any | null + */ + "JMPtr": any | null; + + /** + * string + */ + "TM": string; + + /** + * string | null + */ + "TMPtr": string | null; + + /** + * any + */ + "CJM": any; + + /** + * any | null + */ + "CJMPtr": any | null; + + /** + * string + */ + "CTM": string; + + /** + * string | null + */ + "CTMPtr": string | null; + + /** + * any + */ + "CM": any; + + /** + * any | null + */ + "CMPtr": any | null; + "ANM": AliasNonMarshaler; + + /** + * AliasNonMarshaler | null + */ + "ANMPtr": AliasNonMarshaler | null; + "AJM": AliasJsonMarshaler; + + /** + * AliasJsonMarshaler | null + */ + "AJMPtr": AliasJsonMarshaler | null; + "ATM": AliasTextMarshaler; + + /** + * AliasTextMarshaler | null + */ + "ATMPtr": AliasTextMarshaler | null; + "AM": AliasMarshaler; + + /** + * AliasMarshaler | null + */ + "AMPtr": AliasMarshaler | null; + "ImJM": ImplicitJsonMarshaler; + + /** + * ImplicitJsonMarshaler | null + */ + "ImJMPtr": ImplicitJsonMarshaler | null; + "ImTM": ImplicitTextMarshaler; + + /** + * ImplicitTextMarshaler | null + */ + "ImTMPtr": ImplicitTextMarshaler | null; + "ImM": ImplicitMarshaler; + + /** + * ImplicitMarshaler | null + */ + "ImMPtr": ImplicitMarshaler | null; + "ImNJ": ImplicitNonJson; + + /** + * ImplicitNonJson | null + */ + "ImNJPtr": ImplicitNonJson | null; + "ImNT": ImplicitNonText; + + /** + * ImplicitNonText | null + */ + "ImNTPtr": ImplicitNonText | null; + "ImNM": ImplicitNonMarshaler; + + /** + * ImplicitNonMarshaler | null + */ + "ImNMPtr": ImplicitNonMarshaler | null; + "ImJbT": ImplicitJsonButText; + + /** + * ImplicitJsonButText | null + */ + "ImJbTPtr": ImplicitJsonButText | null; + "ImTbJ": ImplicitTextButJson; + + /** + * ImplicitTextButJson | null + */ + "ImTbJPtr": ImplicitTextButJson | null; + + /** Creates a new Data instance. */ + constructor($$source: Partial = {}) { + if (!("NM" in $$source)) { + this["NM"] = (new NonMarshaler()); + } + if (!("NMPtr" in $$source)) { + this["NMPtr"] = null; + } + if (!("VJM" in $$source)) { + this["VJM"] = null; + } + if (!("VJMPtr" in $$source)) { + this["VJMPtr"] = null; + } + if (!("PJM" in $$source)) { + this["PJM"] = null; + } + if (!("PJMPtr" in $$source)) { + this["PJMPtr"] = null; + } + if (!("VTM" in $$source)) { + this["VTM"] = ""; + } + if (!("VTMPtr" in $$source)) { + this["VTMPtr"] = null; + } + if (!("PTM" in $$source)) { + this["PTM"] = ""; + } + if (!("PTMPtr" in $$source)) { + this["PTMPtr"] = null; + } + if (!("VM" in $$source)) { + this["VM"] = null; + } + if (!("VMPtr" in $$source)) { + this["VMPtr"] = null; + } + if (!("PM" in $$source)) { + this["PM"] = null; + } + if (!("PMPtr" in $$source)) { + this["PMPtr"] = null; + } + if (!("UJM" in $$source)) { + this["UJM"] = null; + } + if (!("UJMPtr" in $$source)) { + this["UJMPtr"] = null; + } + if (!("UTM" in $$source)) { + this["UTM"] = ""; + } + if (!("UTMPtr" in $$source)) { + this["UTMPtr"] = null; + } + if (!("UM" in $$source)) { + this["UM"] = null; + } + if (!("UMPtr" in $$source)) { + this["UMPtr"] = null; + } + if (!("JM" in $$source)) { + this["JM"] = null; + } + if (!("JMPtr" in $$source)) { + this["JMPtr"] = null; + } + if (!("TM" in $$source)) { + this["TM"] = ""; + } + if (!("TMPtr" in $$source)) { + this["TMPtr"] = null; + } + if (!("CJM" in $$source)) { + this["CJM"] = null; + } + if (!("CJMPtr" in $$source)) { + this["CJMPtr"] = null; + } + if (!("CTM" in $$source)) { + this["CTM"] = ""; + } + if (!("CTMPtr" in $$source)) { + this["CTMPtr"] = null; + } + if (!("CM" in $$source)) { + this["CM"] = null; + } + if (!("CMPtr" in $$source)) { + this["CMPtr"] = null; + } + if (!("ANM" in $$source)) { + this["ANM"] = {}; + } + if (!("ANMPtr" in $$source)) { + this["ANMPtr"] = null; + } + if (!("AJM" in $$source)) { + this["AJM"] = null; + } + if (!("AJMPtr" in $$source)) { + this["AJMPtr"] = null; + } + if (!("ATM" in $$source)) { + this["ATM"] = ""; + } + if (!("ATMPtr" in $$source)) { + this["ATMPtr"] = null; + } + if (!("AM" in $$source)) { + this["AM"] = null; + } + if (!("AMPtr" in $$source)) { + this["AMPtr"] = null; + } + if (!("ImJM" in $$source)) { + this["ImJM"] = null; + } + if (!("ImJMPtr" in $$source)) { + this["ImJMPtr"] = null; + } + if (!("ImTM" in $$source)) { + this["ImTM"] = ""; + } + if (!("ImTMPtr" in $$source)) { + this["ImTMPtr"] = null; + } + if (!("ImM" in $$source)) { + this["ImM"] = null; + } + if (!("ImMPtr" in $$source)) { + this["ImMPtr"] = null; + } + if (!("ImNJ" in $$source)) { + this["ImNJ"] = ""; + } + if (!("ImNJPtr" in $$source)) { + this["ImNJPtr"] = null; + } + if (!("ImNT" in $$source)) { + this["ImNT"] = null; + } + if (!("ImNTPtr" in $$source)) { + this["ImNTPtr"] = null; + } + if (!("ImNM" in $$source)) { + this["ImNM"] = (new ImplicitNonMarshaler()); + } + if (!("ImNMPtr" in $$source)) { + this["ImNMPtr"] = null; + } + if (!("ImJbT" in $$source)) { + this["ImJbT"] = null; + } + if (!("ImJbTPtr" in $$source)) { + this["ImJbTPtr"] = null; + } + if (!("ImTbJ" in $$source)) { + this["ImTbJ"] = null; + } + if (!("ImTbJPtr" in $$source)) { + this["ImTbJPtr"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Data instance from a string or object. + */ + static createFrom($$source: any = {}): Data { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType1; + const $$createField48_0 = $$createType2; + const $$createField49_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("NM" in $$parsedSource) { + $$parsedSource["NM"] = $$createField0_0($$parsedSource["NM"]); + } + if ("NMPtr" in $$parsedSource) { + $$parsedSource["NMPtr"] = $$createField1_0($$parsedSource["NMPtr"]); + } + if ("ImNM" in $$parsedSource) { + $$parsedSource["ImNM"] = $$createField48_0($$parsedSource["ImNM"]); + } + if ("ImNMPtr" in $$parsedSource) { + $$parsedSource["ImNMPtr"] = $$createField49_0($$parsedSource["ImNMPtr"]); + } + return new Data($$parsedSource as Partial); + } +} + +/** + * any + */ +export type ImplicitJsonButText = any; + +/** + * any + */ +export type ImplicitJsonMarshaler = any; + +/** + * any + */ +export type ImplicitMarshaler = any; + +/** + * string + */ +export type ImplicitNonJson = string; + +/** + * class{ Marshaler, TextMarshaler } + */ +export class ImplicitNonMarshaler { + "Marshaler": json$0.Marshaler; + "TextMarshaler": encoding$0.TextMarshaler; + + /** Creates a new ImplicitNonMarshaler instance. */ + constructor($$source: Partial = {}) { + if (!("Marshaler" in $$source)) { + this["Marshaler"] = null; + } + if (!("TextMarshaler" in $$source)) { + this["TextMarshaler"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new ImplicitNonMarshaler instance from a string or object. + */ + static createFrom($$source: any = {}): ImplicitNonMarshaler { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new ImplicitNonMarshaler($$parsedSource as Partial); + } +} + +/** + * any + */ +export type ImplicitNonText = any; + +/** + * any + */ +export type ImplicitTextButJson = any; + +/** + * string + */ +export type ImplicitTextMarshaler = string; + +/** + * class {} + */ +export class NonMarshaler { + + /** Creates a new NonMarshaler instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new NonMarshaler instance from a string or object. + */ + static createFrom($$source: any = {}): NonMarshaler { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new NonMarshaler($$parsedSource as Partial); + } +} + +/** + * any + */ +export type PointerJsonMarshaler = any; + +/** + * any + */ +export type PointerMarshaler = any; + +/** + * string + */ +export type PointerTextMarshaler = string; + +/** + * any + */ +export type UnderlyingJsonMarshaler = any; + +/** + * any + */ +export type UnderlyingMarshaler = any; + +/** + * string + */ +export type UnderlyingTextMarshaler = string; + +/** + * any + */ +export type ValueJsonMarshaler = any; + +/** + * any + */ +export type ValueMarshaler = any; + +/** + * string + */ +export type ValueTextMarshaler = string; + +// Private type creation functions +const $$createType0 = NonMarshaler.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = ImplicitNonMarshaler.createFrom; +const $$createType3 = $Create.Nullable($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts new file mode 100644 index 000000000..8e2af391b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts @@ -0,0 +1,19 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Data> { + return $Call.ByName("main.Service.Method").then(($result: any) => { + return $$createType0($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Data.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts new file mode 100644 index 000000000..19d990dbf --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts @@ -0,0 +1,14 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +export { + HowDifferent, + Impersonator, + Person, + PrivatePerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts new file mode 100644 index 000000000..4cb328e1e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts @@ -0,0 +1,163 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + */ +export class HowDifferent { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": { [_: string]: How }[]; + + /** Creates a new HowDifferent instance. */ + constructor($$source: Partial> = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class HowDifferent. + */ + static createFrom($$createParamHow: (source: any) => How): ($$source?: any) => HowDifferent { + const $$createField1_0 = $$createType1($$createParamHow); + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new HowDifferent($$parsedSource as Partial>); + }; + } +} + +/** + * Impersonator gets their fields from other people. + */ +export const Impersonator = other$0.OtherPerson; + +/** + * Impersonator gets their fields from other people. + */ +export type Impersonator = other$0.OtherPerson; + +/** + * Person is not a number. + */ +export class Person { + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField1_0($$parsedSource["Friends"]); + } + return new Person($$parsedSource as Partial); + } +} + +export class personImpl { + /** + * Nickname conceals a person's identity. + */ + "Nickname": string; + + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; + + /** Creates a new personImpl instance. */ + constructor($$source: Partial = {}) { + if (!("Nickname" in $$source)) { + this["Nickname"] = ""; + } + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Friends" in $$source)) { + this["Friends"] = Array.from({ length: 4 }, () => (new Impersonator())); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new personImpl instance from a string or object. + */ + static createFrom($$source: any = {}): personImpl { + const $$createField2_0 = $$createType3; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Friends" in $$parsedSource) { + $$parsedSource["Friends"] = $$createField2_0($$parsedSource["Friends"]); + } + return new personImpl($$parsedSource as Partial); + } +} + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export const PrivatePerson = personImpl; + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export type PrivatePerson = personImpl; + +// Private type creation functions +const $$createType0 = ($$createParamHow: any) => $Create.Map($Create.Any, $$createParamHow); +const $$createType1 = ($$createParamHow: any) => $Create.Array($$createType0($$createParamHow)); +const $$createType2 = other$0.OtherPerson.createFrom($Create.Any); +const $$createType3 = $Create.Array($$createType2); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts new file mode 100644 index 000000000..d2d973b28 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +export { + OtherPerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts new file mode 100644 index 000000000..711735a2b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts @@ -0,0 +1,52 @@ +// 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 "/wails/runtime.js"; + +/** + * OtherPerson is like a person, but different. + */ +export class OtherPerson { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": T[]; + + /** Creates a new OtherPerson instance. */ + constructor($$source: Partial> = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Differences" in $$source)) { + this["Differences"] = []; + } + + Object.assign(this, $$source); + } + + /** + * Given creation functions for each type parameter, + * returns a creation function for a concrete instance + * of the generic class OtherPerson. + */ + static createFrom($$createParamT: (source: any) => T): ($$source?: any) => OtherPerson { + const $$createField1_0 = $$createType0($$createParamT); + return ($$source: any = {}) => { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Differences" in $$parsedSource) { + $$parsedSource["Differences"] = $$createField1_0($$parsedSource["Differences"]); + } + return new OtherPerson($$parsedSource as Partial>); + }; + } +} + +// Private type creation functions +const $$createType0 = ($$createParamT: any) => $Create.Array($$createParamT); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts new file mode 100644 index 000000000..2e4c173df --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other.OtherMethods.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts new file mode 100644 index 000000000..fb5f5ce21 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts @@ -0,0 +1,39 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOne").then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + }); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOtherOne"); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $models.HowDifferent.createFrom($Create.Any); +const $$createType2 = $models.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts new file mode 100644 index 000000000..c82f44866 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @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 "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("main.EmbedOther.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts new file mode 100644 index 000000000..e3b2fe679 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts @@ -0,0 +1,39 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]> { + return $Call.ByName("main.EmbedService.LikeThisOne").then(($result: any) => { + $result[0] = $$createType0($result[0]); + $result[1] = $$createType1($result[1]); + $result[2] = $$createType2($result[2]); + return $result; + }); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("main.EmbedService.LikeThisOtherOne"); +} + +// Private type creation functions +const $$createType0 = nobindingshere$0.Person.createFrom; +const $$createType1 = nobindingshere$0.HowDifferent.createFrom($Create.Any); +const $$createType2 = nobindingshere$0.personImpl.createFrom; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts new file mode 100644 index 000000000..192340e8e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet($0: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts new file mode 100644 index 000000000..e69bebf3b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts new file mode 100644 index 000000000..460b2b374 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts new file mode 100644 index 000000000..e437314f4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts new file mode 100644 index 000000000..460b2b374 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts new file mode 100644 index 000000000..e437314f4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts new file mode 100644 index 000000000..1a2ffb266 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts new file mode 100644 index 000000000..d76f68d9d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +export class Person { + "Name": string; + "Address": services$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts new file mode 100644 index 000000000..fc2efb6c1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services.OtherService.Yay").then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts new file mode 100644 index 000000000..ea2dcf8a6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts @@ -0,0 +1,209 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number }): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null }): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] }): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] }): $CancellablePromise<{ [_: `${number}`]: number[] }> { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in).then(($result: any) => { + return $$createType1($result); + }); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +export function StringArrayInputNamedOutput($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputNamedOutputs($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringArrayOut($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringOut($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in).then(($result: any) => { + return $$createType3($result); + }); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in).then(($result: any) => { + return $$createType4($result); + }); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts new file mode 100644 index 000000000..cd282c90c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +export class Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + this["Parent"] = null; + } + if (!("Details" in $$source)) { + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts new file mode 100644 index 000000000..ea2dcf8a6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts @@ -0,0 +1,209 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number }): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null }): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] }): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] }): $CancellablePromise<{ [_: `${number}`]: number[] }> { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in).then(($result: any) => { + return $$createType1($result); + }); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +export function StringArrayInputNamedOutput($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputNamedOutputs($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringArrayOut($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in).then(($result: any) => { + return $$createType2($result); + }); +} + +export function StringArrayInputStringOut($in: string[]): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in).then(($result: any) => { + return $$createType3($result); + }); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in).then(($result: any) => { + return $$createType4($result); + }); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} + +// Private type creation functions +const $$createType0 = $Create.Array($Create.Any); +const $$createType1 = $Create.Map($Create.Any, $$createType0); +const $$createType2 = $Create.Array($Create.Any); +const $$createType3 = $models.Person.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts new file mode 100644 index 000000000..cd282c90c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts @@ -0,0 +1,43 @@ +// 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 "/wails/runtime.js"; + +export class Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Parent" in $$source)) { + this["Parent"] = null; + } + if (!("Details" in $$source)) { + this["Details"] = {"Age": 0, "Address": {"Street": ""}}; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Parent" in $$parsedSource) { + $$parsedSource["Parent"] = $$createField1_0($$parsedSource["Parent"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts new file mode 100644 index 000000000..6a6a881dc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts new file mode 100644 index 000000000..6a6a881dc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts new file mode 100644 index 000000000..1a2ffb266 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name).then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Person.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts new file mode 100644 index 000000000..f95c1b558 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts new file mode 100644 index 000000000..f6eee9de8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts @@ -0,0 +1,47 @@ +// 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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + */ +export class Person { + "Name": string; + "Address": services$0.Address | null; + + /** Creates a new Person instance. */ + constructor($$source: Partial = {}) { + if (!("Name" in $$source)) { + this["Name"] = ""; + } + if (!("Address" in $$source)) { + this["Address"] = null; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Person instance from a string or object. + */ + static createFrom($$source: any = {}): Person { + const $$createField1_0 = $$createType1; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("Address" in $$parsedSource) { + $$parsedSource["Address"] = $$createField1_0($$parsedSource["Address"]); + } + return new Person($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = services$0.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts new file mode 100644 index 000000000..e4d86b717 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts new file mode 100644 index 000000000..a4be6e904 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts @@ -0,0 +1,35 @@ +// 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 "/wails/runtime.js"; + +export class Address { + "Street": string; + "State": string; + "Country": string; + + /** Creates a new Address instance. */ + constructor($$source: Partial
= {}) { + if (!("Street" in $$source)) { + this["Street"] = ""; + } + if (!("State" in $$source)) { + this["State"] = ""; + } + if (!("Country" in $$source)) { + this["Country"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Address instance from a string or object. + */ + static createFrom($$source: any = {}): Address { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Address($$parsedSource as Partial
); + } +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts new file mode 100644 index 000000000..554897ea4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @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 "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services.OtherService.Yay").then(($result: any) => { + return $$createType1($result); + }); +} + +// Private type creation functions +const $$createType0 = $models.Address.createFrom; +const $$createType1 = $Create.Nullable($$createType0); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/warnings.log b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=false/UseNames=true/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/index.ts new file mode 100644 index 000000000..ba2885969 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + TextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/json/index.ts new file mode 100644 index 000000000..00ec01151 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/json/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Marshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/json/models.ts new file mode 100644 index 000000000..8e7f0b3f1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/json/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + */ +export type Marshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/models.ts new file mode 100644 index 000000000..51089b14d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/encoding/models.ts @@ -0,0 +1,10 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + */ +export type TextMarshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts new file mode 100644 index 000000000..c371520b0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts @@ -0,0 +1,55 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + */ +export function Get(aliasValue: $models.Alias): $CancellablePromise<$models.Person> { + return $Call.ByID(1928502664, aliasValue); +} + +/** + * Apparently, aliases are all the rage right now. + */ +export function GetButAliased(p: $models.AliasedPerson): $CancellablePromise<$models.StrangelyAliasedPerson> { + return $Call.ByID(1896499664, p); +} + +/** + * Get someone quite different. + */ +export function GetButDifferent(): $CancellablePromise<$models.GenericPerson> { + return $Call.ByID(2240931744); +} + +export function GetButForeignPrivateAlias(): $CancellablePromise { + return $Call.ByID(643456960); +} + +export function GetButGenericAliases(): $CancellablePromise<$models.AliasGroup> { + return $Call.ByID(914093800); +} + +/** + * Greet a lot of unusual things. + */ +export function Greet($0: $models.EmptyAliasStruct, $1: $models.EmptyStruct): $CancellablePromise<$models.AliasStruct> { + return $Call.ByID(1411160069, $0, $1); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts new file mode 100644 index 000000000..75cbdc737 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts @@ -0,0 +1,26 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Alias, + AliasGroup, + AliasStruct, + AliasedPerson, + EmptyAliasStruct, + EmptyStruct, + GenericAlias, + GenericMapAlias, + GenericPerson, + GenericPersonAlias, + GenericPtrAlias, + IndirectPersonAlias, + OtherAliasStruct, + Person, + StrangelyAliasedPerson, + TPIndirectPersonAlias +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts new file mode 100644 index 000000000..26b204c1f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts @@ -0,0 +1,125 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * A nice type Alias. + */ +export type Alias = number; + +/** + * A class whose fields have various aliased types. + */ +export interface AliasGroup { + "GAi": GenericAlias; + "GAP": GenericAlias>; + "GPAs": GenericPtrAlias; + "GPAP": GenericPtrAlias>; + "GMA": GenericMapAlias; + "GPA": GenericPersonAlias; + "IPA": IndirectPersonAlias; + "TPIPA": TPIndirectPersonAlias; +} + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + */ +export interface AliasStruct { + /** + * A field with a comment. + */ + "Foo": number[] | null; + + /** + * Definitely not Foo. + */ + "Bar"?: string; + "Baz"?: string; + + /** + * A nested alias struct. + */ + "Other": OtherAliasStruct; +} + +/** + * A class alias. + */ +export type AliasedPerson = Person; + +/** + * An empty struct alias. + */ +export interface EmptyAliasStruct { +} + +/** + * An empty struct. + */ +export interface EmptyStruct { +} + +/** + * A generic alias that forwards to a type parameter. + */ +export type GenericAlias = T; + +/** + * A generic alias that wraps a map. + */ +export type GenericMapAlias = { [_: string]: U } | null; + +/** + * A generic struct containing an alias. + */ +export interface GenericPerson { + "Name": T; + "AliasedField": Alias; +} + +/** + * A generic alias that wraps a generic struct. + */ +export type GenericPersonAlias = GenericPerson[] | null>; + +/** + * A generic alias that wraps a pointer type. + */ +export type GenericPtrAlias = GenericAlias | null; + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export type IndirectPersonAlias = GenericPersonAlias; + +/** + * Another struct alias. + */ +export interface OtherAliasStruct { + "NoMoreIdeas": number[] | null; +} + +/** + * A non-generic struct containing an alias. + */ +export interface Person { + /** + * The Person's name. + */ + "Name": string; + + /** + * A random alias field. + */ + "AliasedField": Alias; +} + +/** + * Another class alias, but ordered after its aliased class. + */ +export type StrangelyAliasedPerson = Person; + +/** + * An alias that wraps a class through a typeparam alias. + */ +export type TPIndirectPersonAlias = GenericAlias>; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts new file mode 100644 index 000000000..bdcf43c67 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts new file mode 100644 index 000000000..5cba569c9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function TestMethod(): $CancellablePromise { + return $Call.ByID(2241101727); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts new file mode 100644 index 000000000..dc425cfd5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function TestMethod2(): $CancellablePromise { + return $Call.ByID(1556848345); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts new file mode 100644 index 000000000..a5334fce3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(person: $models.Person, emb: $models.Embedded1): $CancellablePromise { + return $Call.ByID(1411160069, person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts new file mode 100644 index 000000000..f645e5730 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts @@ -0,0 +1,17 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Title +} from "./models.js"; + +export type { + Embedded1, + Embedded3, + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts new file mode 100644 index 000000000..7cdb88164 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts @@ -0,0 +1,121 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Embedded1 { + /** + * Friends should be shadowed in Person by a field of lesser depth + */ + "Friends": number; + + /** + * Vanish should be omitted from Person because there is another field with same depth and no tag + */ + "Vanish": number; + + /** + * StillThere should be shadowed in Person by other field with same depth and a json tag + */ + "StillThere": string; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; +} + +export type Embedded3 = string; + +/** + * Person represents a person + */ +export interface Person { + /** + * Titles is optional in JSON + */ + "Titles"?: Title[] | null; + + /** + * Names has a + * multiline comment + */ + "Names": string[] | null; + + /** + * Partner has a custom and complex JSON key + */ + "Partner": Person | null; + "Friends": (Person | null)[] | null; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * StillThereButRenamed should shadow in Person the other field with same depth and no json tag + */ + "StillThere": Embedded3 | null; + + /** + * StrangeNumber maps to "-" + */ + "-": number; + + /** + * Embedded3 should appear with key "Embedded3" + */ + "Embedded3": Embedded3; + + /** + * StrangerNumber is serialized as a string + */ + "StrangerNumber": `${number}`; + + /** + * StrangestString is optional and serialized as a JSON string + */ + "StrangestString"?: `"${string}"`; + + /** + * StringStrangest is serialized as a JSON string and optional + */ + "StringStrangest"?: `"${string}"`; + + /** + * embedded4 should be optional and appear with key "emb4" + */ + "emb4"?: embedded4; +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; + +export interface embedded4 { + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * Friends should not be shadowed in Person as embedded4 is not embedded + * from encoding/json's point of view; + * however, it should be shadowed in Embedded1 + */ + "Friends": boolean; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts new file mode 100644 index 000000000..99ffd566f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts @@ -0,0 +1,24 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + */ +export function Greet(str: string, people: $models.Person[] | null, $2: {"AnotherCount": number, "AnotherOne": $models.Person | null}, assoc: { [_: `${number}`]: boolean | null } | null, $4: (number | null)[] | null, ...other: string[]): $CancellablePromise<[$models.Person, any, number[] | null]> { + return $Call.ByID(1411160069, str, people, $2, assoc, $4, other); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts new file mode 100644 index 000000000..6691fb86c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Person represents a person + */ +export interface Person { + "Name": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts new file mode 100644 index 000000000..e6a04adf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.StructA, $models.StructC]> { + return $Call.ByID(440020721); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts new file mode 100644 index 000000000..bbf592890 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + StructA, + StructC, + StructE +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts new file mode 100644 index 000000000..256987ac8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts @@ -0,0 +1,21 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface StructA { + "B": structB | null; +} + +export interface StructC { + "D": structD; +} + +export interface StructE { +} + +export interface structB { + "A": StructA | null; +} + +export interface structD { + "E": StructE; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts new file mode 100644 index 000000000..965a057ca --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]> { + return $Call.ByID(440020721); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts new file mode 100644 index 000000000..16cef660c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Alias, + Cyclic, + GenericCyclic +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts new file mode 100644 index 000000000..5395305fc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type Alias = Cyclic | null; + +export type Cyclic = ({ [_: string]: Alias } | null)[] | null; + +export type GenericCyclic = {"X": GenericCyclic | null, "Y": T[] | null}[] | null; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts new file mode 100644 index 000000000..bb2426022 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Interfaces!"); +console.log("Hello TS!"); +console.log("Hello TS Interfaces!"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts new file mode 100644 index 000000000..9d2a673d6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.InternalModel): $CancellablePromise { + return $Call.ByID(538079117, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts new file mode 100644 index 000000000..efe76e58b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts @@ -0,0 +1,16 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal model. + */ +export interface InternalModel { + "Field": string; +} + +/** + * An unexported model. + */ +export interface unexportedModel { + "Field": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts new file mode 100644 index 000000000..82d89784a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Dummy +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts new file mode 100644 index 000000000..71070d754 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts @@ -0,0 +1,5 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Dummy { +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts new file mode 100644 index 000000000..e60cbea0c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts @@ -0,0 +1,22 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +function InternalMethod($0: string): $CancellablePromise { + return $Call.ByID(3518775569, $0); +} + +export function VisibleMethod($0: otherpackage$0.Dummy): $CancellablePromise { + return $Call.ByID(474018228, $0); +} + +export async function CustomMethod(arg: string): Promise { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js new file mode 100644 index 000000000..442f20472 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts new file mode 100644 index 000000000..253d3f2f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ti.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ti.ts new file mode 100644 index 000000000..7400e97aa --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ti.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts new file mode 100644 index 000000000..57d7f73be --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.unexportedModel): $CancellablePromise { + return $Call.ByID(37626172, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts new file mode 100644 index 000000000..5a3127774 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts @@ -0,0 +1,47 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Comment 1. + */ +export function Method1(): $CancellablePromise { + return $Call.ByID(841558284); +} + +/** + * Comment 2. + */ +export function Method2(): $CancellablePromise { + return $Call.ByID(891891141); +} + +/** + * Comment 3a. + * Comment 3b. + */ +export function Method3(): $CancellablePromise { + return $Call.ByID(875113522); +} + +/** + * Comment 4. + */ +export function Method4(): $CancellablePromise { + return $Call.ByID(791225427); +} + +/** + * Comment 5. + */ +export function Method5(): $CancellablePromise { + return $Call.ByID(774447808); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts new file mode 100644 index 000000000..eda1dd8d0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: $models.Title): $CancellablePromise { + return $Call.ByID(1411160069, name, title); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts new file mode 100644 index 000000000..e66941c16 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts @@ -0,0 +1,16 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Title +} from "./models.js"; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts new file mode 100644 index 000000000..74d673e16 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts @@ -0,0 +1,55 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Age is an integer with some predefined values + */ +export type Age = number; + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + */ +export interface Person { + "Title": Title; + "Name": string; + "Age": Age; +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts new file mode 100644 index 000000000..ade2383a0 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: services$0.Title): $CancellablePromise { + return $Call.ByID(1411160069, name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts new file mode 100644 index 000000000..01c612edc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts new file mode 100644 index 000000000..887aee9ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts new file mode 100644 index 000000000..8519667d5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts new file mode 100644 index 000000000..99b989f07 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts @@ -0,0 +1,14 @@ +// 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 * as services$0 from "./services/models.js"; + +/** + * Person is a person + */ +export interface Person { + "Name": string; + "Address": services$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts new file mode 100644 index 000000000..4cb206cc6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(2007737399); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts new file mode 100644 index 000000000..8519667d5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts new file mode 100644 index 000000000..70b85519b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts @@ -0,0 +1,11 @@ +// 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 * as other$0 from "./services/other/models.js"; + +export interface Person { + "Name": string; + "Address": other$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts new file mode 100644 index 000000000..8879fcfa2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(2447353446); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts new file mode 100644 index 000000000..34c4d151a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts new file mode 100644 index 000000000..8a2cb7a70 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts new file mode 100644 index 000000000..f9b8d87e2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts @@ -0,0 +1,25 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * Greet someone + */ +export function GreetWithContext(name: string): $CancellablePromise { + return $Call.ByID(1310150960, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts new file mode 100644 index 000000000..8a2cb7a70 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts new file mode 100644 index 000000000..e3aeb8a64 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts @@ -0,0 +1,30 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export type { + BasicCstrAlias, + ComparableCstrAlias, + EmbeddedCustomInterface, + EmbeddedOriginalInterface, + EmbeddedPointer, + EmbeddedPointerPtr, + EmbeddedValue, + EmbeddedValuePtr, + GoodTildeCstrAlias, + InterfaceCstrAlias, + Maps, + MixedCstrAlias, + NonBasicCstrAlias, + PointableCstrAlias, + PointerAlias, + PointerTextMarshaler, + StringAlias, + StringType, + ValueAlias, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts new file mode 100644 index 000000000..aaabd3502 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts @@ -0,0 +1,767 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type BasicCstrAlias = S; + +export type ComparableCstrAlias = R; + +export type EmbeddedCustomInterface = string; + +export type EmbeddedOriginalInterface = string; + +export type EmbeddedPointer = string; + +export type EmbeddedPointerPtr = string; + +export type EmbeddedValue = string; + +export type EmbeddedValuePtr = string; + +export type GoodTildeCstrAlias = U; + +export type InterfaceCstrAlias = Y; + +export interface Maps { + /** + * Reject + */ + "Bool": { [_: string]: number } | null; + + /** + * Accept + */ + "Int": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "Uint": { [_: `${number}`]: number } | null; + + /** + * Reject + */ + "Float": { [_: string]: number } | null; + + /** + * Reject + */ + "Complex": { [_: string]: number } | null; + + /** + * Accept + */ + "Byte": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "Rune": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "String": { [_: string]: number } | null; + + /** + * Reject + */ + "IntPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "UintPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "FloatPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "ComplexPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "StringPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "NTM": { [_: string]: number } | null; + + /** + * Reject + */ + "NTMPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "VTM": { [_: ValueTextMarshaler]: number } | null; + + /** + * Accept + */ + "VTMPtr": { [_: ValueTextMarshaler]: number } | null; + + /** + * Reject + */ + "PTM": { [_: string]: number } | null; + + /** + * Accept + */ + "PTMPtr": { [_: PointerTextMarshaler]: number } | null; + + /** + * Accept, hide + */ + "JTM": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "JTMPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "A": { [_: string]: number } | null; + + /** + * Reject + */ + "APtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TM": { [_: string]: number } | null; + + /** + * Reject + */ + "TMPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "CI": { [_: string]: number } | null; + + /** + * Reject + */ + "CIPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "EI": { [_: string]: number } | null; + + /** + * Reject + */ + "EIPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "EV": { [_: EmbeddedValue]: number } | null; + + /** + * Accept + */ + "EVPtr": { [_: EmbeddedValue]: number } | null; + + /** + * Accept + */ + "EVP": { [_: EmbeddedValuePtr]: number } | null; + + /** + * Accept + */ + "EVPPtr": { [_: EmbeddedValuePtr]: number } | null; + + /** + * Reject + */ + "EP": { [_: string]: number } | null; + + /** + * Accept + */ + "EPPtr": { [_: EmbeddedPointer]: number } | null; + + /** + * Accept + */ + "EPP": { [_: EmbeddedPointerPtr]: number } | null; + + /** + * Accept + */ + "EPPPtr": { [_: EmbeddedPointerPtr]: number } | null; + + /** + * Accept + */ + "ECI": { [_: EmbeddedCustomInterface]: number } | null; + + /** + * Accept + */ + "ECIPtr": { [_: EmbeddedCustomInterface]: number } | null; + + /** + * Accept + */ + "EOI": { [_: EmbeddedOriginalInterface]: number } | null; + + /** + * Accept + */ + "EOIPtr": { [_: EmbeddedOriginalInterface]: number } | null; + + /** + * Reject + */ + "WT": { [_: string]: number } | null; + + /** + * Reject + */ + "WA": { [_: string]: number } | null; + + /** + * Accept + */ + "ST": { [_: StringType]: number } | null; + + /** + * Accept + */ + "SA": { [_: StringAlias]: number } | null; + + /** + * Accept + */ + "IntT": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "IntA": { [_: `${number}`]: number } | null; + + /** + * Reject + */ + "VT": { [_: string]: number } | null; + + /** + * Reject + */ + "VTPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "VPT": { [_: string]: number } | null; + + /** + * Reject + */ + "VPTPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "VA": { [_: ValueAlias]: number } | null; + + /** + * Accept + */ + "VAPtr": { [_: ValueAlias]: number } | null; + + /** + * Accept, hide + */ + "VPA": { [_: string]: number } | null; + + /** + * Reject + */ + "VPAPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "PT": { [_: string]: number } | null; + + /** + * Reject + */ + "PTPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "PPT": { [_: string]: number } | null; + + /** + * Reject + */ + "PPTPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "PA": { [_: string]: number } | null; + + /** + * Accept + */ + "PAPtr": { [_: PointerAlias]: number } | null; + + /** + * Accept, hide + */ + "PPA": { [_: string]: number } | null; + + /** + * Reject + */ + "PPAPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "IT": { [_: string]: number } | null; + + /** + * Reject + */ + "ITPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "IPT": { [_: string]: number } | null; + + /** + * Reject + */ + "IPTPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "IA": { [_: string]: number } | null; + + /** + * Reject + */ + "IAPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "IPA": { [_: string]: number } | null; + + /** + * Reject + */ + "IPAPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPR": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPRPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPS": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPSPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPT": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPTPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPU": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPUPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPV": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPVPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPW": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPWPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPX": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPXPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPY": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPYPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPZ": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPZPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAR": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GARPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAS": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GASPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAT": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GATPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAU": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAUPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAV": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAVPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAW": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAWPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAX": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAXPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAY": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAYPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAZ": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAZPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GACi": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "GACV": { [_: ComparableCstrAlias]: number } | null; + + /** + * Reject + */ + "GACP": { [_: string]: number } | null; + + /** + * Reject + */ + "GACiPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GACVPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GACPPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GABi": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "GABs": { [_: BasicCstrAlias]: number } | null; + + /** + * Reject + */ + "GABiPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GABT": { [_: string]: number } | null; + + /** + * Reject + */ + "GABTPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "GAGT": { [_: GoodTildeCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GAGTPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "GANBV": { [_: NonBasicCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GANBP": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GANBVPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GANBPPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "GAPlV1": { [_: PointableCstrAlias]: number } | null; + + /** + * Accept + */ + "GAPlV2": { [_: PointableCstrAlias]: number } | null; + + /** + * Reject + */ + "GAPlP1": { [_: string]: number } | null; + + /** + * Accept + */ + "GAPlP2": { [_: PointableCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GAPlVPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAPlPPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAMi": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "GAMS": { [_: MixedCstrAlias]: number } | null; + + /** + * Accept + */ + "GAMV": { [_: MixedCstrAlias]: number } | null; + + /** + * Reject + */ + "GAMSPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAMVPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAII": { [_: string]: number } | null; + + /** + * Accept + */ + "GAIV": { [_: InterfaceCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GAIP": { [_: string]: number } | null; + + /** + * Reject + */ + "GAIIPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAIVPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GAIPPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAPrV": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAPrP": { [_: string]: number } | null; + + /** + * Reject + */ + "GAPrVPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GAPrPPtr": { [_: string]: number } | null; +} + +export type MixedCstrAlias = X; + +export type NonBasicCstrAlias = V; + +export type PointableCstrAlias = W; + +export type PointerAlias = PointerTextMarshaler; + +export type PointerTextMarshaler = string; + +export type StringAlias = string; + +export type StringType = string; + +export type ValueAlias = ValueTextMarshaler; + +export type ValueTextMarshaler = string; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts new file mode 100644 index 000000000..50d2f6d72 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts @@ -0,0 +1,14 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>> { + return $Call.ByID(4021345184); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts new file mode 100644 index 000000000..7c55b767b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts @@ -0,0 +1,33 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export type { + AliasJsonMarshaler, + AliasMarshaler, + AliasNonMarshaler, + AliasTextMarshaler, + Data, + ImplicitJsonButText, + ImplicitJsonMarshaler, + ImplicitMarshaler, + ImplicitNonJson, + ImplicitNonMarshaler, + ImplicitNonText, + ImplicitTextButJson, + ImplicitTextMarshaler, + NonMarshaler, + PointerJsonMarshaler, + PointerMarshaler, + PointerTextMarshaler, + UnderlyingJsonMarshaler, + UnderlyingMarshaler, + UnderlyingTextMarshaler, + ValueJsonMarshaler, + ValueMarshaler, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts new file mode 100644 index 000000000..02d639994 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts @@ -0,0 +1,309 @@ +// 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 * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + */ +export type AliasJsonMarshaler = any; + +/** + * any + */ +export type AliasMarshaler = any; + +/** + * struct{} + */ +export interface AliasNonMarshaler { +} + +/** + * string + */ +export type AliasTextMarshaler = string; + +export interface Data { + "NM": NonMarshaler; + + /** + * NonMarshaler | null + */ + "NMPtr": NonMarshaler | null; + "VJM": ValueJsonMarshaler; + + /** + * ValueJsonMarshaler | null + */ + "VJMPtr": ValueJsonMarshaler | null; + "PJM": PointerJsonMarshaler; + + /** + * PointerJsonMarshaler | null + */ + "PJMPtr": PointerJsonMarshaler | null; + "VTM": ValueTextMarshaler; + + /** + * ValueTextMarshaler | null + */ + "VTMPtr": ValueTextMarshaler | null; + "PTM": PointerTextMarshaler; + + /** + * PointerTextMarshaler | null + */ + "PTMPtr": PointerTextMarshaler | null; + "VM": ValueMarshaler; + + /** + * ValueMarshaler | null + */ + "VMPtr": ValueMarshaler | null; + "PM": PointerMarshaler; + + /** + * PointerMarshaler | null + */ + "PMPtr": PointerMarshaler | null; + "UJM": UnderlyingJsonMarshaler; + + /** + * UnderlyingJsonMarshaler | null + */ + "UJMPtr": UnderlyingJsonMarshaler | null; + "UTM": UnderlyingTextMarshaler; + + /** + * UnderlyingTextMarshaler | null + */ + "UTMPtr": UnderlyingTextMarshaler | null; + "UM": UnderlyingMarshaler; + + /** + * UnderlyingMarshaler | null + */ + "UMPtr": UnderlyingMarshaler | null; + + /** + * any + */ + "JM": any; + + /** + * any | null + */ + "JMPtr": any | null; + + /** + * string + */ + "TM": string; + + /** + * string | null + */ + "TMPtr": string | null; + + /** + * any + */ + "CJM": any; + + /** + * any | null + */ + "CJMPtr": any | null; + + /** + * string + */ + "CTM": string; + + /** + * string | null + */ + "CTMPtr": string | null; + + /** + * any + */ + "CM": any; + + /** + * any | null + */ + "CMPtr": any | null; + "ANM": AliasNonMarshaler; + + /** + * AliasNonMarshaler | null + */ + "ANMPtr": AliasNonMarshaler | null; + "AJM": AliasJsonMarshaler; + + /** + * AliasJsonMarshaler | null + */ + "AJMPtr": AliasJsonMarshaler | null; + "ATM": AliasTextMarshaler; + + /** + * AliasTextMarshaler | null + */ + "ATMPtr": AliasTextMarshaler | null; + "AM": AliasMarshaler; + + /** + * AliasMarshaler | null + */ + "AMPtr": AliasMarshaler | null; + "ImJM": ImplicitJsonMarshaler; + + /** + * ImplicitJsonMarshaler | null + */ + "ImJMPtr": ImplicitJsonMarshaler | null; + "ImTM": ImplicitTextMarshaler; + + /** + * ImplicitTextMarshaler | null + */ + "ImTMPtr": ImplicitTextMarshaler | null; + "ImM": ImplicitMarshaler; + + /** + * ImplicitMarshaler | null + */ + "ImMPtr": ImplicitMarshaler | null; + "ImNJ": ImplicitNonJson; + + /** + * ImplicitNonJson | null + */ + "ImNJPtr": ImplicitNonJson | null; + "ImNT": ImplicitNonText; + + /** + * ImplicitNonText | null + */ + "ImNTPtr": ImplicitNonText | null; + "ImNM": ImplicitNonMarshaler; + + /** + * ImplicitNonMarshaler | null + */ + "ImNMPtr": ImplicitNonMarshaler | null; + "ImJbT": ImplicitJsonButText; + + /** + * ImplicitJsonButText | null + */ + "ImJbTPtr": ImplicitJsonButText | null; + "ImTbJ": ImplicitTextButJson; + + /** + * ImplicitTextButJson | null + */ + "ImTbJPtr": ImplicitTextButJson | null; +} + +/** + * any + */ +export type ImplicitJsonButText = any; + +/** + * any + */ +export type ImplicitJsonMarshaler = any; + +/** + * any + */ +export type ImplicitMarshaler = any; + +/** + * string + */ +export type ImplicitNonJson = string; + +/** + * class{ Marshaler, TextMarshaler } + */ +export interface ImplicitNonMarshaler { + "Marshaler": json$0.Marshaler; + "TextMarshaler": encoding$0.TextMarshaler; +} + +/** + * any + */ +export type ImplicitNonText = any; + +/** + * any + */ +export type ImplicitTextButJson = any; + +/** + * string + */ +export type ImplicitTextMarshaler = string; + +/** + * class {} + */ +export interface NonMarshaler { +} + +/** + * any + */ +export type PointerJsonMarshaler = any; + +/** + * any + */ +export type PointerMarshaler = any; + +/** + * string + */ +export type PointerTextMarshaler = string; + +/** + * any + */ +export type UnderlyingJsonMarshaler = any; + +/** + * any + */ +export type UnderlyingMarshaler = any; + +/** + * string + */ +export type UnderlyingTextMarshaler = string; + +/** + * any + */ +export type ValueJsonMarshaler = any; + +/** + * any + */ +export type ValueMarshaler = any; + +/** + * string + */ +export type ValueTextMarshaler = string; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts new file mode 100644 index 000000000..b175ebe96 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts @@ -0,0 +1,14 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Data> { + return $Call.ByID(4021345184); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts new file mode 100644 index 000000000..bf611e486 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts @@ -0,0 +1,14 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +export type { + HowDifferent, + Impersonator, + Person, + PrivatePerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts new file mode 100644 index 000000000..c42cce373 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts @@ -0,0 +1,64 @@ +// 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 * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + */ +export interface HowDifferent { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": ({ [_: string]: How } | null)[] | null; +} + +/** + * Impersonator gets their fields from other people. + */ +export type Impersonator = other$0.OtherPerson; + +/** + * Person is not a number. + */ +export interface Person { + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; +} + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export type PrivatePerson = personImpl; + +export interface personImpl { + /** + * Nickname conceals a person's identity. + */ + "Nickname": string; + + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts new file mode 100644 index 000000000..b9f2889db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +export type { + OtherPerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts new file mode 100644 index 000000000..6ca5b82a2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts @@ -0,0 +1,17 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherPerson is like a person, but different. + */ +export interface OtherPerson { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": T[] | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts new file mode 100644 index 000000000..c2fb7bc6a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(3606939272); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts new file mode 100644 index 000000000..4f623616d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]> { + return $Call.ByID(2124352079); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(4281222271); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts new file mode 100644 index 000000000..d155adc30 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(3566862802); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts new file mode 100644 index 000000000..65098990f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]> { + return $Call.ByID(2590614085); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByID(773650321); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts new file mode 100644 index 000000000..76375250e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet($0: string): $CancellablePromise { + return $Call.ByID(1411160069, $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts new file mode 100644 index 000000000..e69bebf3b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts new file mode 100644 index 000000000..34c4d151a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts new file mode 100644 index 000000000..d27e71304 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts new file mode 100644 index 000000000..34c4d151a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts new file mode 100644 index 000000000..d27e71304 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByID(4249972365); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts new file mode 100644 index 000000000..8519667d5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts new file mode 100644 index 000000000..3ab21d612 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts @@ -0,0 +1,11 @@ +// 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 * as services$0 from "./services/models.js"; + +export interface Person { + "Name": string; + "Address": services$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts new file mode 100644 index 000000000..50e62daa4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(3568225479); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts new file mode 100644 index 000000000..ef5015467 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts @@ -0,0 +1,190 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByID(3862002418, $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByID(2424639793, $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByID(3132595881, $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByID(3306292566, $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1754277916, $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1909469092, $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(4251088558, $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1343888303, $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2205561041, $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByID(572240879, $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2189402897, $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByID(642881729, $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1066151743, $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByID(2718999663, $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByID(2386486356, $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null } | null): $CancellablePromise { + return $Call.ByID(2163571325, $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise { + return $Call.ByID(2900172572, $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise<{ [_: `${number}`]: number[] | null } | null> { + return $Call.ByID(881980169, $in); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByID(1075577233); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByID(3589606958, $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByID(224675106, $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByID(2124953624, $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByID(3516977899, $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByID(229603958, $in); +} + +export function StringArrayInputNamedOutput($in: string[] | null): $CancellablePromise { + return $Call.ByID(3678582682, $in); +} + +export function StringArrayInputNamedOutputs($in: string[] | null): $CancellablePromise { + return $Call.ByID(319259595, $in); +} + +export function StringArrayInputStringArrayOut($in: string[] | null): $CancellablePromise { + return $Call.ByID(383995060, $in); +} + +export function StringArrayInputStringOut($in: string[] | null): $CancellablePromise { + return $Call.ByID(1091960237, $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByID(3835643147, $in); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByID(2447692557, $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByID(2943477349, $in); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(3401034892, $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1236957573, $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(1160383782, $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1739300671, $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(793803239, $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1403757716, $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2988345717, $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(518250834, $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2836661285, $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1367187362, $in); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts new file mode 100644 index 000000000..5664b79ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts new file mode 100644 index 000000000..ef5015467 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts @@ -0,0 +1,190 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByID(3862002418, $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByID(2424639793, $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByID(3132595881, $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByID(2182412247, $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByID(3306292566, $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1754277916, $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1909469092, $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(4251088558, $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByID(1343888303, $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2205561041, $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByID(572240879, $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(2189402897, $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByID(642881729, $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1066151743, $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByID(2718999663, $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByID(2386486356, $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null } | null): $CancellablePromise { + return $Call.ByID(2163571325, $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise { + return $Call.ByID(2900172572, $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise<{ [_: `${number}`]: number[] | null } | null> { + return $Call.ByID(881980169, $in); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByID(1075577233); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByID(3589606958, $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByID(224675106, $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByID(2124953624, $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByID(3516977899, $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByID(229603958, $in); +} + +export function StringArrayInputNamedOutput($in: string[] | null): $CancellablePromise { + return $Call.ByID(3678582682, $in); +} + +export function StringArrayInputNamedOutputs($in: string[] | null): $CancellablePromise { + return $Call.ByID(319259595, $in); +} + +export function StringArrayInputStringArrayOut($in: string[] | null): $CancellablePromise { + return $Call.ByID(383995060, $in); +} + +export function StringArrayInputStringOut($in: string[] | null): $CancellablePromise { + return $Call.ByID(1091960237, $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByID(3835643147, $in); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByID(2447692557, $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByID(2943477349, $in); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(3401034892, $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1236957573, $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(1160383782, $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1739300671, $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(793803239, $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1403757716, $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2988345717, $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(518250834, $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByID(2836661285, $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByID(1367187362, $in); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts new file mode 100644 index 000000000..5664b79ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts new file mode 100644 index 000000000..8a2cb7a70 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts new file mode 100644 index 000000000..8a2cb7a70 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts new file mode 100644 index 000000000..8519667d5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByID(1411160069, name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByID(1661412647, name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts new file mode 100644 index 000000000..f29117203 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts @@ -0,0 +1,15 @@ +// 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 * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + */ +export interface Person { + "Name": string; + "Address": services$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts new file mode 100644 index 000000000..79c8907f9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByID(1491748400); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/warnings.log b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/index.ts new file mode 100644 index 000000000..ba2885969 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + TextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/json/index.ts new file mode 100644 index 000000000..00ec01151 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/json/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Marshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/json/models.ts new file mode 100644 index 000000000..8e7f0b3f1 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/json/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Marshaler is the interface implemented by types that + * can marshal themselves into valid JSON. + */ +export type Marshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/models.ts new file mode 100644 index 000000000..51089b14d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/encoding/models.ts @@ -0,0 +1,10 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * TextMarshaler is the interface implemented by an object that can + * marshal itself into a textual form. + * + * MarshalText encodes the receiver into UTF-8-encoded text and returns the result. + */ +export type TextMarshaler = any; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts new file mode 100644 index 000000000..d19c65d22 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/greetservice.ts @@ -0,0 +1,55 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Get someone. + */ +export function Get(aliasValue: $models.Alias): $CancellablePromise<$models.Person> { + return $Call.ByName("main.GreetService.Get", aliasValue); +} + +/** + * Apparently, aliases are all the rage right now. + */ +export function GetButAliased(p: $models.AliasedPerson): $CancellablePromise<$models.StrangelyAliasedPerson> { + return $Call.ByName("main.GreetService.GetButAliased", p); +} + +/** + * Get someone quite different. + */ +export function GetButDifferent(): $CancellablePromise<$models.GenericPerson> { + return $Call.ByName("main.GreetService.GetButDifferent"); +} + +export function GetButForeignPrivateAlias(): $CancellablePromise { + return $Call.ByName("main.GreetService.GetButForeignPrivateAlias"); +} + +export function GetButGenericAliases(): $CancellablePromise<$models.AliasGroup> { + return $Call.ByName("main.GreetService.GetButGenericAliases"); +} + +/** + * Greet a lot of unusual things. + */ +export function Greet($0: $models.EmptyAliasStruct, $1: $models.EmptyStruct): $CancellablePromise<$models.AliasStruct> { + return $Call.ByName("main.GreetService.Greet", $0, $1); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts new file mode 100644 index 000000000..75cbdc737 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/index.ts @@ -0,0 +1,26 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Alias, + AliasGroup, + AliasStruct, + AliasedPerson, + EmptyAliasStruct, + EmptyStruct, + GenericAlias, + GenericMapAlias, + GenericPerson, + GenericPersonAlias, + GenericPtrAlias, + IndirectPersonAlias, + OtherAliasStruct, + Person, + StrangelyAliasedPerson, + TPIndirectPersonAlias +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts new file mode 100644 index 000000000..26b204c1f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/aliases/models.ts @@ -0,0 +1,125 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * A nice type Alias. + */ +export type Alias = number; + +/** + * A class whose fields have various aliased types. + */ +export interface AliasGroup { + "GAi": GenericAlias; + "GAP": GenericAlias>; + "GPAs": GenericPtrAlias; + "GPAP": GenericPtrAlias>; + "GMA": GenericMapAlias; + "GPA": GenericPersonAlias; + "IPA": IndirectPersonAlias; + "TPIPA": TPIndirectPersonAlias; +} + +/** + * A struct alias. + * This should be rendered as a typedef or interface in every mode. + */ +export interface AliasStruct { + /** + * A field with a comment. + */ + "Foo": number[] | null; + + /** + * Definitely not Foo. + */ + "Bar"?: string; + "Baz"?: string; + + /** + * A nested alias struct. + */ + "Other": OtherAliasStruct; +} + +/** + * A class alias. + */ +export type AliasedPerson = Person; + +/** + * An empty struct alias. + */ +export interface EmptyAliasStruct { +} + +/** + * An empty struct. + */ +export interface EmptyStruct { +} + +/** + * A generic alias that forwards to a type parameter. + */ +export type GenericAlias = T; + +/** + * A generic alias that wraps a map. + */ +export type GenericMapAlias = { [_: string]: U } | null; + +/** + * A generic struct containing an alias. + */ +export interface GenericPerson { + "Name": T; + "AliasedField": Alias; +} + +/** + * A generic alias that wraps a generic struct. + */ +export type GenericPersonAlias = GenericPerson[] | null>; + +/** + * A generic alias that wraps a pointer type. + */ +export type GenericPtrAlias = GenericAlias | null; + +/** + * An alias that wraps a class through a non-typeparam alias. + */ +export type IndirectPersonAlias = GenericPersonAlias; + +/** + * Another struct alias. + */ +export interface OtherAliasStruct { + "NoMoreIdeas": number[] | null; +} + +/** + * A non-generic struct containing an alias. + */ +export interface Person { + /** + * The Person's name. + */ + "Name": string; + + /** + * A random alias field. + */ + "AliasedField": Alias; +} + +/** + * Another class alias, but ordered after its aliased class. + */ +export type StrangelyAliasedPerson = Person; + +/** + * An alias that wraps a class through a typeparam alias. + */ +export type TPIndirectPersonAlias = GenericAlias>; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts new file mode 100644 index 000000000..bdcf43c67 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service7 from "./service7.js"; +import * as Service9 from "./service9.js"; +export { + Service7, + Service9 +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts new file mode 100644 index 000000000..e4281d925 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service7.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function TestMethod(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service7.TestMethod"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts new file mode 100644 index 000000000..1c0154b5e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config/service9.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function TestMethod2(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/complex_expressions/config.Service9.TestMethod2"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts new file mode 100644 index 000000000..9eff81e94 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(person: $models.Person, emb: $models.Embedded1): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", person, emb); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts new file mode 100644 index 000000000..f645e5730 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/index.ts @@ -0,0 +1,17 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Title +} from "./models.js"; + +export type { + Embedded1, + Embedded3, + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts new file mode 100644 index 000000000..7cdb88164 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_json/models.ts @@ -0,0 +1,121 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Embedded1 { + /** + * Friends should be shadowed in Person by a field of lesser depth + */ + "Friends": number; + + /** + * Vanish should be omitted from Person because there is another field with same depth and no tag + */ + "Vanish": number; + + /** + * StillThere should be shadowed in Person by other field with same depth and a json tag + */ + "StillThere": string; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; +} + +export type Embedded3 = string; + +/** + * Person represents a person + */ +export interface Person { + /** + * Titles is optional in JSON + */ + "Titles"?: Title[] | null; + + /** + * Names has a + * multiline comment + */ + "Names": string[] | null; + + /** + * Partner has a custom and complex JSON key + */ + "Partner": Person | null; + "Friends": (Person | null)[] | null; + + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * StillThereButRenamed should shadow in Person the other field with same depth and no json tag + */ + "StillThere": Embedded3 | null; + + /** + * StrangeNumber maps to "-" + */ + "-": number; + + /** + * Embedded3 should appear with key "Embedded3" + */ + "Embedded3": Embedded3; + + /** + * StrangerNumber is serialized as a string + */ + "StrangerNumber": `${number}`; + + /** + * StrangestString is optional and serialized as a JSON string + */ + "StrangestString"?: `"${string}"`; + + /** + * StringStrangest is serialized as a JSON string and optional + */ + "StringStrangest"?: `"${string}"`; + + /** + * embedded4 should be optional and appear with key "emb4" + */ + "emb4"?: embedded4; +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; + +export interface embedded4 { + /** + * NamingThingsIsHard is a law of programming + */ + "NamingThingsIsHard": `${boolean}`; + + /** + * Friends should not be shadowed in Person as embedded4 is not embedded + * from encoding/json's point of view; + * however, it should be shadowed in Embedded1 + */ + "Friends": boolean; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts new file mode 100644 index 000000000..0c3ef75cb --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/greetservice.ts @@ -0,0 +1,24 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + * It has a multiline doc comment + * The comment has even some * / traps!! + */ +export function Greet(str: string, people: $models.Person[] | null, $2: {"AnotherCount": number, "AnotherOne": $models.Person | null}, assoc: { [_: `${number}`]: boolean | null } | null, $4: (number | null)[] | null, ...other: string[]): $CancellablePromise<[$models.Person, any, number[] | null]> { + return $Call.ByName("main.GreetService.Greet", str, people, $2, assoc, $4, other); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts new file mode 100644 index 000000000..6691fb86c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/complex_method/models.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Person represents a person + */ +export interface Person { + "Name": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts new file mode 100644 index 000000000..52f87997c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.StructA, $models.StructC]> { + return $Call.ByName("main.GreetService.MakeCycles"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts new file mode 100644 index 000000000..bbf592890 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + StructA, + StructC, + StructE +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts new file mode 100644 index 000000000..256987ac8 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_imports/models.ts @@ -0,0 +1,21 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface StructA { + "B": structB | null; +} + +export interface StructC { + "D": structD; +} + +export interface StructE { +} + +export interface structB { + "A": StructA | null; +} + +export interface structD { + "E": StructE; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts new file mode 100644 index 000000000..b7ef0ae3e --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Make a cycle. + */ +export function MakeCycles(): $CancellablePromise<[$models.Cyclic, $models.GenericCyclic<$models.GenericCyclic>]> { + return $Call.ByName("main.GreetService.MakeCycles"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts new file mode 100644 index 000000000..16cef660c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Alias, + Cyclic, + GenericCyclic +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts new file mode 100644 index 000000000..5395305fc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/cyclic_types/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type Alias = Cyclic | null; + +export type Cyclic = ({ [_: string]: Alias } | null)[] | null; + +export type GenericCyclic = {"X": GenericCyclic | null, "Y": T[] | null}[] | null; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts new file mode 100644 index 000000000..bb2426022 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +console.log("Hello everywhere!"); +console.log("Hello everywhere again!"); +console.log("Hello Interfaces!"); +console.log("Hello TS!"); +console.log("Hello TS Interfaces!"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts new file mode 100644 index 000000000..faca61bc2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/internalservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.InternalModel): $CancellablePromise { + return $Call.ByName("main.InternalService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts new file mode 100644 index 000000000..efe76e58b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/models.ts @@ -0,0 +1,16 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An exported but internal model. + */ +export interface InternalModel { + "Field": string; +} + +/** + * An unexported model. + */ +export interface unexportedModel { + "Field": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts new file mode 100644 index 000000000..82d89784a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Dummy +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts new file mode 100644 index 000000000..71070d754 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage/models.ts @@ -0,0 +1,5 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Dummy { +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts new file mode 100644 index 000000000..84ac0538c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/service.ts @@ -0,0 +1,22 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as otherpackage$0 from "./otherpackage/models.js"; + +function InternalMethod($0: string): $CancellablePromise { + return $Call.ByName("main.Service.InternalMethod", $0); +} + +export function VisibleMethod($0: otherpackage$0.Dummy): $CancellablePromise { + return $Call.ByName("main.Service.VisibleMethod", $0); +} + +export async function CustomMethod(arg: string): Promise { + await InternalMethod("Hello " + arg + "!"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js new file mode 100644 index 000000000..138385f53 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js new file mode 100644 index 000000000..19d5c2f42 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_all.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("everywhere again"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js new file mode 100644 index 000000000..442f20472 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_i.js @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts new file mode 100644 index 000000000..253d3f2f6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_t.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ti.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ti.ts new file mode 100644 index 000000000..7400e97aa --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/test_ti.ts @@ -0,0 +1,3 @@ +import { CustomMethod } from "./service.js"; + +CustomMethod("TS Interfaces"); diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts new file mode 100644 index 000000000..b49239ada --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/directives/unexportedservice.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * An unexported service. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method($0: $models.unexportedModel): $CancellablePromise { + return $Call.ByName("main.unexportedService.Method", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts new file mode 100644 index 000000000..4951af01d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/greetservice.ts @@ -0,0 +1,47 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Comment 1. + */ +export function Method1(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method1"); +} + +/** + * Comment 2. + */ +export function Method2(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method2"); +} + +/** + * Comment 3a. + * Comment 3b. + */ +export function Method3(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method3"); +} + +/** + * Comment 4. + */ +export function Method4(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method4"); +} + +/** + * Comment 5. + */ +export function Method5(): $CancellablePromise { + return $Call.ByName("main.GreetService.Method5"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/embedded_interface/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts new file mode 100644 index 000000000..d857aa9e7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: $models.Title): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name, title); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts new file mode 100644 index 000000000..e66941c16 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/index.ts @@ -0,0 +1,16 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export { + Age, + Title +} from "./models.js"; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts new file mode 100644 index 000000000..74d673e16 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum/models.ts @@ -0,0 +1,55 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Age is an integer with some predefined values + */ +export type Age = number; + +/** + * Predefined constants for type Age. + * @namespace + */ +export const Age = { + NewBorn: 0, + Teenager: 12, + YoungAdult: 18, + + /** + * Oh no, some grey hair! + */ + MiddleAged: 50, + + /** + * Unbelievable! + */ + Mathusalem: 1000, +}; + +/** + * Person represents a person + */ +export interface Person { + "Title": Title; + "Name": string; + "Age": Age; +} + +/** + * Title is a title + */ +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts new file mode 100644 index 000000000..73469c39d --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/greetservice.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as services$0 from "./services/models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string, title: services$0.Title): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name, title); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts new file mode 100644 index 000000000..01c612edc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + Title +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts new file mode 100644 index 000000000..887aee9ba --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/enum_from_imported_package/services/models.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export enum Title { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Mister is a title + */ + Mister = "Mr", + Miss = "Miss", + Ms = "Ms", + Mrs = "Mrs", + Dr = "Dr", +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts new file mode 100644 index 000000000..5a8e9adc3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts new file mode 100644 index 000000000..99b989f07 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/models.ts @@ -0,0 +1,14 @@ +// 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 * as services$0 from "./services/models.js"; + +/** + * Person is a person + */ +export interface Person { + "Name": string; + "Address": services$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts new file mode 100644 index 000000000..c6db3803b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_imported_package/services.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts new file mode 100644 index 000000000..5a8e9adc3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts new file mode 100644 index 000000000..70b85519b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/models.ts @@ -0,0 +1,11 @@ +// 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 * as other$0 from "./services/other/models.js"; + +export interface Person { + "Name": string; + "Address": other$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts new file mode 100644 index 000000000..b83ce5234 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/function_from_nested_imported_package/services/other.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts new file mode 100644 index 000000000..41664b850 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_multiple_files/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts new file mode 100644 index 000000000..80bebe425 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts new file mode 100644 index 000000000..63a65248c --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/greetservice.ts @@ -0,0 +1,25 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * Greet someone + */ +export function GreetWithContext(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.GreetWithContext", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_context/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts new file mode 100644 index 000000000..80bebe425 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts new file mode 100644 index 000000000..e3aeb8a64 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/index.ts @@ -0,0 +1,30 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export type { + BasicCstrAlias, + ComparableCstrAlias, + EmbeddedCustomInterface, + EmbeddedOriginalInterface, + EmbeddedPointer, + EmbeddedPointerPtr, + EmbeddedValue, + EmbeddedValuePtr, + GoodTildeCstrAlias, + InterfaceCstrAlias, + Maps, + MixedCstrAlias, + NonBasicCstrAlias, + PointableCstrAlias, + PointerAlias, + PointerTextMarshaler, + StringAlias, + StringType, + ValueAlias, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts new file mode 100644 index 000000000..aaabd3502 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/models.ts @@ -0,0 +1,767 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type BasicCstrAlias = S; + +export type ComparableCstrAlias = R; + +export type EmbeddedCustomInterface = string; + +export type EmbeddedOriginalInterface = string; + +export type EmbeddedPointer = string; + +export type EmbeddedPointerPtr = string; + +export type EmbeddedValue = string; + +export type EmbeddedValuePtr = string; + +export type GoodTildeCstrAlias = U; + +export type InterfaceCstrAlias = Y; + +export interface Maps { + /** + * Reject + */ + "Bool": { [_: string]: number } | null; + + /** + * Accept + */ + "Int": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "Uint": { [_: `${number}`]: number } | null; + + /** + * Reject + */ + "Float": { [_: string]: number } | null; + + /** + * Reject + */ + "Complex": { [_: string]: number } | null; + + /** + * Accept + */ + "Byte": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "Rune": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "String": { [_: string]: number } | null; + + /** + * Reject + */ + "IntPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "UintPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "FloatPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "ComplexPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "StringPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "NTM": { [_: string]: number } | null; + + /** + * Reject + */ + "NTMPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "VTM": { [_: ValueTextMarshaler]: number } | null; + + /** + * Accept + */ + "VTMPtr": { [_: ValueTextMarshaler]: number } | null; + + /** + * Reject + */ + "PTM": { [_: string]: number } | null; + + /** + * Accept + */ + "PTMPtr": { [_: PointerTextMarshaler]: number } | null; + + /** + * Accept, hide + */ + "JTM": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "JTMPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "A": { [_: string]: number } | null; + + /** + * Reject + */ + "APtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TM": { [_: string]: number } | null; + + /** + * Reject + */ + "TMPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "CI": { [_: string]: number } | null; + + /** + * Reject + */ + "CIPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "EI": { [_: string]: number } | null; + + /** + * Reject + */ + "EIPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "EV": { [_: EmbeddedValue]: number } | null; + + /** + * Accept + */ + "EVPtr": { [_: EmbeddedValue]: number } | null; + + /** + * Accept + */ + "EVP": { [_: EmbeddedValuePtr]: number } | null; + + /** + * Accept + */ + "EVPPtr": { [_: EmbeddedValuePtr]: number } | null; + + /** + * Reject + */ + "EP": { [_: string]: number } | null; + + /** + * Accept + */ + "EPPtr": { [_: EmbeddedPointer]: number } | null; + + /** + * Accept + */ + "EPP": { [_: EmbeddedPointerPtr]: number } | null; + + /** + * Accept + */ + "EPPPtr": { [_: EmbeddedPointerPtr]: number } | null; + + /** + * Accept + */ + "ECI": { [_: EmbeddedCustomInterface]: number } | null; + + /** + * Accept + */ + "ECIPtr": { [_: EmbeddedCustomInterface]: number } | null; + + /** + * Accept + */ + "EOI": { [_: EmbeddedOriginalInterface]: number } | null; + + /** + * Accept + */ + "EOIPtr": { [_: EmbeddedOriginalInterface]: number } | null; + + /** + * Reject + */ + "WT": { [_: string]: number } | null; + + /** + * Reject + */ + "WA": { [_: string]: number } | null; + + /** + * Accept + */ + "ST": { [_: StringType]: number } | null; + + /** + * Accept + */ + "SA": { [_: StringAlias]: number } | null; + + /** + * Accept + */ + "IntT": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "IntA": { [_: `${number}`]: number } | null; + + /** + * Reject + */ + "VT": { [_: string]: number } | null; + + /** + * Reject + */ + "VTPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "VPT": { [_: string]: number } | null; + + /** + * Reject + */ + "VPTPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "VA": { [_: ValueAlias]: number } | null; + + /** + * Accept + */ + "VAPtr": { [_: ValueAlias]: number } | null; + + /** + * Accept, hide + */ + "VPA": { [_: string]: number } | null; + + /** + * Reject + */ + "VPAPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "PT": { [_: string]: number } | null; + + /** + * Reject + */ + "PTPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "PPT": { [_: string]: number } | null; + + /** + * Reject + */ + "PPTPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "PA": { [_: string]: number } | null; + + /** + * Accept + */ + "PAPtr": { [_: PointerAlias]: number } | null; + + /** + * Accept, hide + */ + "PPA": { [_: string]: number } | null; + + /** + * Reject + */ + "PPAPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "IT": { [_: string]: number } | null; + + /** + * Reject + */ + "ITPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "IPT": { [_: string]: number } | null; + + /** + * Reject + */ + "IPTPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "IA": { [_: string]: number } | null; + + /** + * Reject + */ + "IAPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "IPA": { [_: string]: number } | null; + + /** + * Reject + */ + "IPAPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPR": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPRPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPS": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPSPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPT": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPTPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPU": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPUPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPV": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPVPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPW": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPWPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPX": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPXPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPY": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPYPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "TPZ": { [_: string]: number } | null; + + /** + * Soft reject + */ + "TPZPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAR": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GARPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAS": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GASPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAT": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GATPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAU": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAUPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAV": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAVPtr": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAW": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAWPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAX": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAXPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAY": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAYPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAZ": { [_: string]: number } | null; + + /** + * Soft reject + */ + "GAZPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GACi": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "GACV": { [_: ComparableCstrAlias]: number } | null; + + /** + * Reject + */ + "GACP": { [_: string]: number } | null; + + /** + * Reject + */ + "GACiPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GACVPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GACPPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GABi": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "GABs": { [_: BasicCstrAlias]: number } | null; + + /** + * Reject + */ + "GABiPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GABT": { [_: string]: number } | null; + + /** + * Reject + */ + "GABTPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "GAGT": { [_: GoodTildeCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GAGTPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "GANBV": { [_: NonBasicCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GANBP": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GANBVPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GANBPPtr": { [_: string]: number } | null; + + /** + * Accept + */ + "GAPlV1": { [_: PointableCstrAlias]: number } | null; + + /** + * Accept + */ + "GAPlV2": { [_: PointableCstrAlias]: number } | null; + + /** + * Reject + */ + "GAPlP1": { [_: string]: number } | null; + + /** + * Accept + */ + "GAPlP2": { [_: PointableCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GAPlVPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAPlPPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAMi": { [_: `${number}`]: number } | null; + + /** + * Accept + */ + "GAMS": { [_: MixedCstrAlias]: number } | null; + + /** + * Accept + */ + "GAMV": { [_: MixedCstrAlias]: number } | null; + + /** + * Reject + */ + "GAMSPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAMVPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAII": { [_: string]: number } | null; + + /** + * Accept + */ + "GAIV": { [_: InterfaceCstrAlias]: number } | null; + + /** + * Accept, hide + */ + "GAIP": { [_: string]: number } | null; + + /** + * Reject + */ + "GAIIPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAIVPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GAIPPtr": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAPrV": { [_: string]: number } | null; + + /** + * Accept, hide + */ + "GAPrP": { [_: string]: number } | null; + + /** + * Reject + */ + "GAPrVPtr": { [_: string]: number } | null; + + /** + * Reject + */ + "GAPrPPtr": { [_: string]: number } | null; +} + +export type MixedCstrAlias = X; + +export type NonBasicCstrAlias = V; + +export type PointableCstrAlias = W; + +export type PointerAlias = PointerTextMarshaler; + +export type PointerTextMarshaler = string; + +export type StringAlias = string; + +export type StringType = string; + +export type ValueAlias = ValueTextMarshaler; + +export type ValueTextMarshaler = string; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts new file mode 100644 index 000000000..7b59b53ed --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys/service.ts @@ -0,0 +1,14 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Maps<$models.PointerTextMarshaler, number, number, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null, $models.ValueTextMarshaler, $models.StringType, $models.ValueTextMarshaler, $models.PointerTextMarshaler | null>> { + return $Call.ByName("main.Service.Method"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts new file mode 100644 index 000000000..7c55b767b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/index.ts @@ -0,0 +1,33 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export type { + AliasJsonMarshaler, + AliasMarshaler, + AliasNonMarshaler, + AliasTextMarshaler, + Data, + ImplicitJsonButText, + ImplicitJsonMarshaler, + ImplicitMarshaler, + ImplicitNonJson, + ImplicitNonMarshaler, + ImplicitNonText, + ImplicitTextButJson, + ImplicitTextMarshaler, + NonMarshaler, + PointerJsonMarshaler, + PointerMarshaler, + PointerTextMarshaler, + UnderlyingJsonMarshaler, + UnderlyingMarshaler, + UnderlyingTextMarshaler, + ValueJsonMarshaler, + ValueMarshaler, + ValueTextMarshaler +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts new file mode 100644 index 000000000..02d639994 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/models.ts @@ -0,0 +1,309 @@ +// 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 * as encoding$0 from "../../../../../../../../encoding/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as json$0 from "../../../../../../../../encoding/json/models.js"; + +/** + * any + */ +export type AliasJsonMarshaler = any; + +/** + * any + */ +export type AliasMarshaler = any; + +/** + * struct{} + */ +export interface AliasNonMarshaler { +} + +/** + * string + */ +export type AliasTextMarshaler = string; + +export interface Data { + "NM": NonMarshaler; + + /** + * NonMarshaler | null + */ + "NMPtr": NonMarshaler | null; + "VJM": ValueJsonMarshaler; + + /** + * ValueJsonMarshaler | null + */ + "VJMPtr": ValueJsonMarshaler | null; + "PJM": PointerJsonMarshaler; + + /** + * PointerJsonMarshaler | null + */ + "PJMPtr": PointerJsonMarshaler | null; + "VTM": ValueTextMarshaler; + + /** + * ValueTextMarshaler | null + */ + "VTMPtr": ValueTextMarshaler | null; + "PTM": PointerTextMarshaler; + + /** + * PointerTextMarshaler | null + */ + "PTMPtr": PointerTextMarshaler | null; + "VM": ValueMarshaler; + + /** + * ValueMarshaler | null + */ + "VMPtr": ValueMarshaler | null; + "PM": PointerMarshaler; + + /** + * PointerMarshaler | null + */ + "PMPtr": PointerMarshaler | null; + "UJM": UnderlyingJsonMarshaler; + + /** + * UnderlyingJsonMarshaler | null + */ + "UJMPtr": UnderlyingJsonMarshaler | null; + "UTM": UnderlyingTextMarshaler; + + /** + * UnderlyingTextMarshaler | null + */ + "UTMPtr": UnderlyingTextMarshaler | null; + "UM": UnderlyingMarshaler; + + /** + * UnderlyingMarshaler | null + */ + "UMPtr": UnderlyingMarshaler | null; + + /** + * any + */ + "JM": any; + + /** + * any | null + */ + "JMPtr": any | null; + + /** + * string + */ + "TM": string; + + /** + * string | null + */ + "TMPtr": string | null; + + /** + * any + */ + "CJM": any; + + /** + * any | null + */ + "CJMPtr": any | null; + + /** + * string + */ + "CTM": string; + + /** + * string | null + */ + "CTMPtr": string | null; + + /** + * any + */ + "CM": any; + + /** + * any | null + */ + "CMPtr": any | null; + "ANM": AliasNonMarshaler; + + /** + * AliasNonMarshaler | null + */ + "ANMPtr": AliasNonMarshaler | null; + "AJM": AliasJsonMarshaler; + + /** + * AliasJsonMarshaler | null + */ + "AJMPtr": AliasJsonMarshaler | null; + "ATM": AliasTextMarshaler; + + /** + * AliasTextMarshaler | null + */ + "ATMPtr": AliasTextMarshaler | null; + "AM": AliasMarshaler; + + /** + * AliasMarshaler | null + */ + "AMPtr": AliasMarshaler | null; + "ImJM": ImplicitJsonMarshaler; + + /** + * ImplicitJsonMarshaler | null + */ + "ImJMPtr": ImplicitJsonMarshaler | null; + "ImTM": ImplicitTextMarshaler; + + /** + * ImplicitTextMarshaler | null + */ + "ImTMPtr": ImplicitTextMarshaler | null; + "ImM": ImplicitMarshaler; + + /** + * ImplicitMarshaler | null + */ + "ImMPtr": ImplicitMarshaler | null; + "ImNJ": ImplicitNonJson; + + /** + * ImplicitNonJson | null + */ + "ImNJPtr": ImplicitNonJson | null; + "ImNT": ImplicitNonText; + + /** + * ImplicitNonText | null + */ + "ImNTPtr": ImplicitNonText | null; + "ImNM": ImplicitNonMarshaler; + + /** + * ImplicitNonMarshaler | null + */ + "ImNMPtr": ImplicitNonMarshaler | null; + "ImJbT": ImplicitJsonButText; + + /** + * ImplicitJsonButText | null + */ + "ImJbTPtr": ImplicitJsonButText | null; + "ImTbJ": ImplicitTextButJson; + + /** + * ImplicitTextButJson | null + */ + "ImTbJPtr": ImplicitTextButJson | null; +} + +/** + * any + */ +export type ImplicitJsonButText = any; + +/** + * any + */ +export type ImplicitJsonMarshaler = any; + +/** + * any + */ +export type ImplicitMarshaler = any; + +/** + * string + */ +export type ImplicitNonJson = string; + +/** + * class{ Marshaler, TextMarshaler } + */ +export interface ImplicitNonMarshaler { + "Marshaler": json$0.Marshaler; + "TextMarshaler": encoding$0.TextMarshaler; +} + +/** + * any + */ +export type ImplicitNonText = any; + +/** + * any + */ +export type ImplicitTextButJson = any; + +/** + * string + */ +export type ImplicitTextMarshaler = string; + +/** + * class {} + */ +export interface NonMarshaler { +} + +/** + * any + */ +export type PointerJsonMarshaler = any; + +/** + * any + */ +export type PointerMarshaler = any; + +/** + * string + */ +export type PointerTextMarshaler = string; + +/** + * any + */ +export type UnderlyingJsonMarshaler = any; + +/** + * any + */ +export type UnderlyingMarshaler = any; + +/** + * string + */ +export type UnderlyingTextMarshaler = string; + +/** + * any + */ +export type ValueJsonMarshaler = any; + +/** + * any + */ +export type ValueMarshaler = any; + +/** + * string + */ +export type ValueTextMarshaler = string; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts new file mode 100644 index 000000000..0de2fecd5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/marshalers/service.ts @@ -0,0 +1,14 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function Method(): $CancellablePromise<$models.Data> { + return $Call.ByName("main.Service.Method"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts new file mode 100644 index 000000000..bf611e486 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/index.ts @@ -0,0 +1,14 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as SomeMethods from "./somemethods.js"; +export { + SomeMethods +}; + +export type { + HowDifferent, + Impersonator, + Person, + PrivatePerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts new file mode 100644 index 000000000..c42cce373 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/models.ts @@ -0,0 +1,64 @@ +// 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 * as other$0 from "./other/models.js"; + +/** + * HowDifferent is a curious kind of person + * that lets other people decide how they are different. + */ +export interface HowDifferent { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": ({ [_: string]: How } | null)[] | null; +} + +/** + * Impersonator gets their fields from other people. + */ +export type Impersonator = other$0.OtherPerson; + +/** + * Person is not a number. + */ +export interface Person { + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; +} + +/** + * PrivatePerson gets their fields from hidden sources. + */ +export type PrivatePerson = personImpl; + +export interface personImpl { + /** + * Nickname conceals a person's identity. + */ + "Nickname": string; + + /** + * They have a name. + */ + "Name": string; + + /** + * Exactly 4 sketchy friends. + */ + "Friends": Impersonator[]; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts new file mode 100644 index 000000000..b9f2889db --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherMethods from "./othermethods.js"; +export { + OtherMethods +}; + +export type { + OtherPerson +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts new file mode 100644 index 000000000..6ca5b82a2 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/models.ts @@ -0,0 +1,17 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherPerson is like a person, but different. + */ +export interface OtherPerson { + /** + * They have a name as well. + */ + "Name": string; + + /** + * But they may have many differences. + */ + "Differences": T[] | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts new file mode 100644 index 000000000..e6638332a --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other/othermethods.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherMethods has another method, but through a private embedded type. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/other.OtherMethods.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts new file mode 100644 index 000000000..e37a596df --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/somemethods.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SomeMethods exports some methods. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[$models.Person, $models.HowDifferent, $models.PrivatePerson]> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOne"); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here.SomeMethods.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts new file mode 100644 index 000000000..ec7619bfc --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedother.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedOther is even trickier. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("main.EmbedOther.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts new file mode 100644 index 000000000..680b028b4 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/embedservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * EmbedService is tricky. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as nobindingshere$0 from "../no_bindings_here/models.js"; + +/** + * LikeThisOne is an example method that does nothing. + */ +export function LikeThisOne(): $CancellablePromise<[nobindingshere$0.Person, nobindingshere$0.HowDifferent, nobindingshere$0.PrivatePerson]> { + return $Call.ByName("main.EmbedService.LikeThisOne"); +} + +/** + * LikeThisOtherOne does nothing as well, but is different. + */ +export function LikeThisOtherOne(): $CancellablePromise { + return $Call.ByName("main.EmbedService.LikeThisOtherOne"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts new file mode 100644 index 000000000..4166b32f7 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet($0: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", $0); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts new file mode 100644 index 000000000..e69bebf3b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/out_of_tree/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as EmbedOther from "./embedother.js"; +import * as EmbedService from "./embedservice.js"; +import * as GreetService from "./greetservice.js"; +export { + EmbedOther, + EmbedService, + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts new file mode 100644 index 000000000..41664b850 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts new file mode 100644 index 000000000..5a733e6c9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts new file mode 100644 index 000000000..41664b850 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/greetservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts new file mode 100644 index 000000000..5c4ebea4f --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/index.ts @@ -0,0 +1,9 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +import * as OtherService from "./otherservice.js"; +export { + GreetService, + OtherService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts new file mode 100644 index 000000000..5a733e6c9 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_files/otherservice.ts @@ -0,0 +1,10 @@ +// 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 { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +export function Hello(): $CancellablePromise { + return $Call.ByName("main.OtherService.Hello"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts new file mode 100644 index 000000000..5a8e9adc3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts new file mode 100644 index 000000000..3ab21d612 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/models.ts @@ -0,0 +1,11 @@ +// 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 * as services$0 from "./services/models.js"; + +export interface Person { + "Name": string; + "Address": services$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts new file mode 100644 index 000000000..018d8df30 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_multiple_other/services.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts new file mode 100644 index 000000000..b8abcb9a6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/greetservice.ts @@ -0,0 +1,190 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise<{ [_: `${number}`]: number[] | null } | null> { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +export function StringArrayInputNamedOutput($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in); +} + +export function StringArrayInputNamedOutputs($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in); +} + +export function StringArrayInputStringArrayOut($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in); +} + +export function StringArrayInputStringOut($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts new file mode 100644 index 000000000..5664b79ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_non_pointer_single/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts new file mode 100644 index 000000000..b8abcb9a6 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/greetservice.ts @@ -0,0 +1,190 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +export function ArrayInt($in: number[]): $CancellablePromise { + return $Call.ByName("main.GreetService.ArrayInt", $in); +} + +export function BoolInBoolOut($in: boolean): $CancellablePromise { + return $Call.ByName("main.GreetService.BoolInBoolOut", $in); +} + +export function Float32InFloat32Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float32InFloat32Out", $in); +} + +export function Float64InFloat64Out($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Float64InFloat64Out", $in); +} + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +export function Int16InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16InIntOut", $in); +} + +export function Int16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int16PointerInAndOutput", $in); +} + +export function Int32InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32InIntOut", $in); +} + +export function Int32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int32PointerInAndOutput", $in); +} + +export function Int64InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64InIntOut", $in); +} + +export function Int64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int64PointerInAndOutput", $in); +} + +export function Int8InIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8InIntOut", $in); +} + +export function Int8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.Int8PointerInAndOutput", $in); +} + +export function IntInIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.IntInIntOut", $in); +} + +export function IntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInAndOutput", $in); +} + +export function IntPointerInputNamedOutputs($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.IntPointerInputNamedOutputs", $in); +} + +export function MapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntInt", $in); +} + +export function MapIntIntPointer($in: { [_: `${number}`]: number | null } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntIntPointer", $in); +} + +export function MapIntSliceInt($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.MapIntSliceInt", $in); +} + +export function MapIntSliceIntInMapIntSliceIntOut($in: { [_: `${number}`]: number[] | null } | null): $CancellablePromise<{ [_: `${number}`]: number[] | null } | null> { + return $Call.ByName("main.GreetService.MapIntSliceIntInMapIntSliceIntOut", $in); +} + +export function NoInputsStringOut(): $CancellablePromise { + return $Call.ByName("main.GreetService.NoInputsStringOut"); +} + +export function PointerBoolInBoolOut($in: boolean | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerBoolInBoolOut", $in); +} + +export function PointerFloat32InFloat32Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat32InFloat32Out", $in); +} + +export function PointerFloat64InFloat64Out($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerFloat64InFloat64Out", $in); +} + +export function PointerMapIntInt($in: { [_: `${number}`]: number } | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerMapIntInt", $in); +} + +export function PointerStringInStringOut($in: string | null): $CancellablePromise { + return $Call.ByName("main.GreetService.PointerStringInStringOut", $in); +} + +export function StringArrayInputNamedOutput($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutput", $in); +} + +export function StringArrayInputNamedOutputs($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputNamedOutputs", $in); +} + +export function StringArrayInputStringArrayOut($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringArrayOut", $in); +} + +export function StringArrayInputStringOut($in: string[] | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StringArrayInputStringOut", $in); +} + +export function StructInputStructOutput($in: $models.Person): $CancellablePromise<$models.Person> { + return $Call.ByName("main.GreetService.StructInputStructOutput", $in); +} + +export function StructPointerInputErrorOutput($in: $models.Person | null): $CancellablePromise { + return $Call.ByName("main.GreetService.StructPointerInputErrorOutput", $in); +} + +export function StructPointerInputStructPointerOutput($in: $models.Person | null): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.StructPointerInputStructPointerOutput", $in); +} + +export function UInt16InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16InUIntOut", $in); +} + +export function UInt16PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt16PointerInAndOutput", $in); +} + +export function UInt32InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32InUIntOut", $in); +} + +export function UInt32PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt32PointerInAndOutput", $in); +} + +export function UInt64InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64InUIntOut", $in); +} + +export function UInt64PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt64PointerInAndOutput", $in); +} + +export function UInt8InUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8InUIntOut", $in); +} + +export function UInt8PointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UInt8PointerInAndOutput", $in); +} + +export function UIntInUIntOut($in: number): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntInUIntOut", $in); +} + +export function UIntPointerInAndOutput($in: number | null): $CancellablePromise { + return $Call.ByName("main.GreetService.UIntPointerInAndOutput", $in); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts new file mode 100644 index 000000000..5664b79ab --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/struct_literal_single/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Person { + "Name": string; + "Parent": Person | null; + "Details": {"Age": number, "Address": {"Street": string}}; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts new file mode 100644 index 000000000..80bebe425 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts new file mode 100644 index 000000000..80bebe425 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/greetservice.ts @@ -0,0 +1,18 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +/** + * Greet someone + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts new file mode 100644 index 000000000..50e3f0435 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_function/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts new file mode 100644 index 000000000..5a8e9adc3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/greetservice.ts @@ -0,0 +1,29 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * GreetService is great + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Greet does XYZ + */ +export function Greet(name: string): $CancellablePromise { + return $Call.ByName("main.GreetService.Greet", name); +} + +/** + * NewPerson creates a new person + */ +export function NewPerson(name: string): $CancellablePromise<$models.Person | null> { + return $Call.ByName("main.GreetService.NewPerson", name); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts new file mode 100644 index 000000000..14252454b --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as GreetService from "./greetservice.js"; +export { + GreetService +}; + +export type { + Person +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts new file mode 100644 index 000000000..f29117203 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/models.ts @@ -0,0 +1,15 @@ +// 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 * as services$0 from "./services/models.js"; + +/** + * Person is a person! + * They have a name and an address + */ +export interface Person { + "Name": string; + "Address": services$0.Address | null; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts new file mode 100644 index 000000000..8857d2bc5 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as OtherService from "./otherservice.js"; +export { + OtherService +}; + +export type { + Address +} from "./models.js"; diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts new file mode 100644 index 000000000..b76080cf3 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/models.ts @@ -0,0 +1,8 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Address { + "Street": string; + "State": string; + "Country": string; +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts new file mode 100644 index 000000000..c93f85314 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services/otherservice.ts @@ -0,0 +1,23 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * OtherService is a struct + * that does things + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * Yay does this and that + */ +export function Yay(): $CancellablePromise<$models.Address | null> { + return $Call.ByName("github.com/wailsapp/wails/v3/internal/generator/testcases/variable_single_from_other_function/services.OtherService.Yay"); +} diff --git a/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/warnings.log b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/warnings.log new file mode 100644 index 000000000..e802b6b63 --- /dev/null +++ b/v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=true/warnings.log @@ -0,0 +1,70 @@ +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *U is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *V is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *X is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Y is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *Z is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *encoding.TextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.CustomInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedInterface is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *int is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *string is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *uint is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type W is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type any is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type bool is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type complex64 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type float32 is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[T] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BadTildeCstrPtrAlias[struct{}] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[S] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.BasicCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[R] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ComparableCstrPtrAlias[int] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.EmbeddedPointer is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.GoodTildeCstrPtrAlias[U] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[Y] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfaceCstrPtrAlias[encoding.TextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.InterfacePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[X] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.MixedCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.StringType] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[*github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonBasicCstrPtrAlias[V] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.NonTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[W] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointableCstrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[R, Z] is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerCstrPtrAlias[github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler, *github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueTextMarshaler] is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerPtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerTextMarshaler is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.PointerType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValuePtrType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.ValueType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongAlias is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors +package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys.WrongType is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors diff --git a/v3/internal/generator/testdata/package-lock.json b/v3/internal/generator/testdata/package-lock.json new file mode 100644 index 000000000..0e30f5fd3 --- /dev/null +++ b/v3/internal/generator/testdata/package-lock.json @@ -0,0 +1,2272 @@ +{ + "name": "testdata", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "testdata", + "version": "0.0.0", + "devDependencies": { + "madge": "^8.0.0", + "typescript": "^5.7.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dependents/detective-less": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-5.0.0.tgz", + "integrity": "sha512-D/9dozteKcutI5OdxJd8rU+fL6XgaaRg60sPPJWkT33OCiRfkCu5wO5B/yXTaaL2e6EB0lcCBGe5E0XscZCvvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@ts-graphviz/adapter": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.6.tgz", + "integrity": "sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/common": "^2.1.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-graphviz/ast": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@ts-graphviz/ast/-/ast-2.0.7.tgz", + "integrity": "sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/common": "^2.1.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-graphviz/common": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ts-graphviz/common/-/common-2.1.5.tgz", + "integrity": "sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-graphviz/core": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@ts-graphviz/core/-/core-2.0.7.tgz", + "integrity": "sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/ast": "^2.0.7", + "@ts-graphviz/common": "^2.1.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ast-module-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-6.0.0.tgz", + "integrity": "sha512-LFRg7178Fw5R4FAEwZxVqiRI8IxSM+Ay2UBrHoCerXNme+kMMMfz7T3xDGV/c2fer87hcrtgJGsnSOfUrPK6ng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dependency-tree": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-11.0.1.tgz", + "integrity": "sha512-eCt7HSKIC9NxgIykG2DRq3Aewn9UhVS14MB3rEn6l/AsEI1FBg6ZGSlCU0SZ6Tjm2kkhj6/8c2pViinuyKELhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.0.0", + "filing-cabinet": "^5.0.1", + "precinct": "^12.0.2", + "typescript": "^5.4.5" + }, + "bin": { + "dependency-tree": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/dependency-tree/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-amd": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-6.0.0.tgz", + "integrity": "sha512-NTqfYfwNsW7AQltKSEaWR66hGkTeD52Kz3eRQ+nfkA9ZFZt3iifRCWh+yZ/m6t3H42JFwVFTrml/D64R2PAIOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^6.0.0", + "escodegen": "^2.1.0", + "get-amd-module-type": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "bin": { + "detective-amd": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-cjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-6.0.0.tgz", + "integrity": "sha512-R55jTS6Kkmy6ukdrbzY4x+I7KkXiuDPpFzUViFV/tm2PBGtTCjkh9ZmTuJc1SaziMHJOe636dtiZLEuzBL9drg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-es6": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-5.0.0.tgz", + "integrity": "sha512-NGTnzjvgeMW1khUSEXCzPDoraLenWbUjCFjwxReH+Ir+P6LGjYtaBbAvITWn2H0VSC+eM7/9LFOTAkrta6hNYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-postcss": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-7.0.0.tgz", + "integrity": "sha512-pSXA6dyqmBPBuERpoOKKTUUjQCZwZPLRbd1VdsTbt6W+m/+6ROl4BbE87yQBUtLoK7yX8pvXHdKyM/xNIW9F7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-url": "^1.2.4", + "postcss-values-parser": "^6.0.2" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/detective-sass": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-6.0.0.tgz", + "integrity": "sha512-h5GCfFMkPm4ZUUfGHVPKNHKT8jV7cSmgK+s4dgQH4/dIUNh9/huR1fjEQrblOQNDalSU7k7g+tiW9LJ+nVEUhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-scss": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-5.0.0.tgz", + "integrity": "sha512-Y64HyMqntdsCh1qAH7ci95dk0nnpA29g319w/5d/oYcHolcGUVJbIhOirOFjfN1KnMAXAFm5FIkZ4l2EKFGgxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-stylus": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-5.0.0.tgz", + "integrity": "sha512-KMHOsPY6aq3196WteVhkY5FF+6Nnc/r7q741E+Gq+Ax9mhE2iwj8Hlw8pl+749hPDRDBHZ2WlgOjP+twIG61vQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-typescript": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-13.0.0.tgz", + "integrity": "sha512-tcMYfiFWoUejSbvSblw90NDt76/4mNftYCX0SMnVRYzSXv8Fvo06hi4JOPdNvVNxRtCAKg3MJ3cBJh+ygEMH+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "^7.6.0", + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "^5.4.4" + } + }, + "node_modules/detective-vue2": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/detective-vue2/-/detective-vue2-2.1.1.tgz", + "integrity": "sha512-/TQ+cs4qmSyhgESjyBXxoUuh36XjS06+UhCItWcGGOpXmU3KBRGRknG+tDzv2dASn1+UJUm2rhpDFa9TWT0dFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dependents/detective-less": "^5.0.0", + "@vue/compiler-sfc": "^3.5.13", + "detective-es6": "^5.0.0", + "detective-sass": "^6.0.0", + "detective-scss": "^5.0.0", + "detective-stylus": "^5.0.0", + "detective-typescript": "^13.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "typescript": "^5.4.4" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/filing-cabinet": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.0.2.tgz", + "integrity": "sha512-RZlFj8lzyu6jqtFBeXNqUjjNG6xm+gwXue3T70pRxw1W40kJwlgq0PSWAmh0nAnn5DHuBIecLXk9+1VKS9ICXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-module-path": "^2.2.0", + "commander": "^12.0.0", + "enhanced-resolve": "^5.16.0", + "module-definition": "^6.0.0", + "module-lookup-amd": "^9.0.1", + "resolve": "^1.22.8", + "resolve-dependency-path": "^4.0.0", + "sass-lookup": "^6.0.1", + "stylus-lookup": "^6.0.0", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.4.4" + }, + "bin": { + "filing-cabinet": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/filing-cabinet/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-amd-module-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-6.0.0.tgz", + "integrity": "sha512-hFM7oivtlgJ3d6XWD6G47l8Wyh/C6vFw5G24Kk1Tbq85yh5gcM8Fne5/lFhiuxB+RT6+SI7I1ThB9lG4FBh3jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "gonzales": "bin/gonzales.js" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-url-superb": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz", + "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/madge": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/madge/-/madge-8.0.0.tgz", + "integrity": "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^7.2.0", + "commondir": "^1.0.1", + "debug": "^4.3.4", + "dependency-tree": "^11.0.0", + "ora": "^5.4.1", + "pluralize": "^8.0.0", + "pretty-ms": "^7.0.1", + "rc": "^1.2.8", + "stream-to-array": "^2.3.0", + "ts-graphviz": "^2.1.2", + "walkdir": "^0.4.1" + }, + "bin": { + "madge": "bin/cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/pahen" + }, + "peerDependencies": { + "typescript": "^5.4.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/module-definition": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-6.0.0.tgz", + "integrity": "sha512-sEGP5nKEXU7fGSZUML/coJbrO+yQtxcppDAYWRE9ovWsTbFoUHB2qDUx564WUzDaBHXsD46JBbIK5WVTwCyu3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "bin": { + "module-definition": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/module-lookup-amd": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-9.0.2.tgz", + "integrity": "sha512-p7PzSVEWiW9fHRX9oM+V4aV5B2nCVddVNv4DZ/JB6t9GsXY4E+ZVhPpnwUX7bbJyGeeVZqhS8q/JZ/H77IqPFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.1.0", + "glob": "^7.2.3", + "requirejs": "^2.3.7", + "requirejs-config-file": "^4.0.0" + }, + "bin": { + "lookup-amd": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/module-lookup-amd/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-source-walk": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-7.0.0.tgz", + "integrity": "sha512-1uiY543L+N7Og4yswvlm5NCKgPKDEXd9AUR9Jh3gen6oOeBsesr6LqhXom1er3eRzSUcVRWXzhv8tSNrIfGHKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.24.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-values-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz", + "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "color-name": "^1.1.4", + "is-url-superb": "^4.0.0", + "quote-unquote": "^1.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.2.9" + } + }, + "node_modules/precinct": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-12.1.2.tgz", + "integrity": "sha512-x2qVN3oSOp3D05ihCd8XdkIPuEQsyte7PSxzLqiRgktu79S5Dr1I75/S+zAup8/0cwjoiJTQztE9h0/sWp9bJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dependents/detective-less": "^5.0.0", + "commander": "^12.1.0", + "detective-amd": "^6.0.0", + "detective-cjs": "^6.0.0", + "detective-es6": "^5.0.0", + "detective-postcss": "^7.0.0", + "detective-sass": "^6.0.0", + "detective-scss": "^5.0.0", + "detective-stylus": "^5.0.0", + "detective-typescript": "^13.0.0", + "detective-vue2": "^2.0.3", + "module-definition": "^6.0.0", + "node-source-walk": "^7.0.0", + "postcss": "^8.4.40", + "typescript": "^5.5.4" + }, + "bin": { + "precinct": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/precinct/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quote-unquote": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz", + "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/requirejs": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz", + "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==", + "dev": true, + "license": "MIT", + "bin": { + "r_js": "bin/r.js", + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/requirejs-config-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz", + "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "^4.0.0", + "stringify-object": "^3.2.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-dependency-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-4.0.0.tgz", + "integrity": "sha512-hlY1SybBGm5aYN3PC4rp15MzsJLM1w+MEA/4KU3UBPfz4S0lL3FL6mgv7JgaA8a+ZTeEQAiF1a1BuN2nkqiIlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sass-lookup": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-6.0.1.tgz", + "integrity": "sha512-nl9Wxbj9RjEJA5SSV0hSDoU2zYGtE+ANaDS4OFUR7nYrquvBFvPKZZtQHe3lvnxCcylEDV00KUijjdMTUElcVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.0.0" + }, + "bin": { + "sass-lookup": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sass-lookup/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-to-array": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", + "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylus-lookup": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-6.0.0.tgz", + "integrity": "sha512-RaWKxAvPnIXrdby+UWCr1WRfa+lrPMSJPySte4Q6a+rWyjeJyFOLJxr5GrAVfcMCsfVlCuzTAJ/ysYT8p8do7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.0.0" + }, + "bin": { + "stylus-lookup": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylus-lookup/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-graphviz": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-2.1.6.tgz", + "integrity": "sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/adapter": "^2.0.6", + "@ts-graphviz/ast": "^2.0.7", + "@ts-graphviz/common": "^2.1.5", + "@ts-graphviz/core": "^2.0.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/walkdir": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", + "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/v3/internal/generator/testdata/package.json b/v3/internal/generator/testdata/package.json new file mode 100644 index 000000000..4ad95cc64 --- /dev/null +++ b/v3/internal/generator/testdata/package.json @@ -0,0 +1,10 @@ +{ + "name": "testdata", + "version": "0.0.0", + "description": "Output from generator testcases. This package.json is here only to pull in the Typescript compiler as a dependency.", + "type": "module", + "devDependencies": { + "typescript": "^5.7.3", + "madge": "^8.0.0" + } +} diff --git a/v3/internal/generator/testdata/tsconfig.json b/v3/internal/generator/testdata/tsconfig.json new file mode 100644 index 000000000..aa4939e6a --- /dev/null +++ b/v3/internal/generator/testdata/tsconfig.json @@ -0,0 +1,35 @@ +{ + "include": ["output/**/*.js", "output/**/*.ts"], + "exclude": ["output/**/*.got.?s"], + "references": [ + { "path": "../../runtime/desktop/@wailsio/runtime" } + ], + "compilerOptions": { + "allowJs": true, + + "noEmit": true, + "skipLibCheck": true, + + "target": "ES2015", + "module": "ES2015", + "moduleResolution": "bundler", + "isolatedModules": true, + "verbatimModuleSyntax": true, + + "lib": [ + "DOM", + "ESNext" + ], + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + + "paths": { + "/wails/runtime.js": ["../../runtime/desktop/@wailsio/runtime/src/index.ts"] + } + } +} diff --git a/v3/internal/github/github.go b/v3/internal/github/github.go new file mode 100644 index 000000000..da534177a --- /dev/null +++ b/v3/internal/github/github.go @@ -0,0 +1,142 @@ +package github + +import ( + "encoding/json" + "fmt" + "github.com/charmbracelet/glamour/styles" + "io" + "net/http" + "net/url" + "sort" + "strings" + + "github.com/charmbracelet/glamour" +) + +func GetReleaseNotes(tagVersion string, noColour bool) string { + resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/releases/tags/" + url.PathEscape(tagVersion)) + if err != nil { + return "Unable to retrieve release notes. Please check your network connection" + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return "Unable to retrieve release notes. Please check your network connection" + } + + data := map[string]interface{}{} + err = json.Unmarshal(body, &data) + if err != nil { + return "Unable to retrieve release notes. Please check your network connection" + } + + if data["body"] == nil { + return "No release notes found" + } + + result := "# Release Notes for " + tagVersion + "\n" + data["body"].(string) + var renderer *glamour.TermRenderer + + if noColour { + renderer, err = glamour.NewTermRenderer(glamour.WithStyles(styles.NoTTYStyleConfig)) + } else { + renderer, err = glamour.NewTermRenderer(glamour.WithAutoStyle()) + } + if err != nil { + return result + } + result, err = renderer.Render(result) + if err != nil { + return err.Error() + } + return result +} + +// GetVersionTags gets the list of tags on the Wails repo +// It returns a list of sorted tags in descending order +func GetVersionTags() ([]*SemanticVersion, error) { + result := []*SemanticVersion{} + var err error + + resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags") + if err != nil { + return result, err + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return result, err + } + + data := []map[string]interface{}{} + err = json.Unmarshal(body, &data) + if err != nil { + return result, err + } + + // Convert tag data to Version structs + for _, tag := range data { + version := tag["name"].(string) + if !strings.HasPrefix(version, "v2") { + continue + } + semver, err := NewSemanticVersion(version) + if err != nil { + return result, err + } + result = append(result, semver) + } + + // Reverse Sort + sort.Sort(sort.Reverse(SemverCollection(result))) + + return result, err +} + +// GetLatestStableRelease gets the latest stable release on GitHub +func GetLatestStableRelease() (result *SemanticVersion, err error) { + tags, err := GetVersionTags() + if err != nil { + return nil, err + } + + for _, tag := range tags { + if tag.IsRelease() { + return tag, nil + } + } + + return nil, fmt.Errorf("no release tag found") +} + +// GetLatestPreRelease gets the latest prerelease on GitHub +func GetLatestPreRelease() (result *SemanticVersion, err error) { + tags, err := GetVersionTags() + if err != nil { + return nil, err + } + + for _, tag := range tags { + if tag.IsPreRelease() { + return tag, nil + } + } + + return nil, fmt.Errorf("no prerelease tag found") +} + +// IsValidTag returns true if the given string is a valid tag +func IsValidTag(tagVersion string) (bool, error) { + if tagVersion[0] == 'v' { + tagVersion = tagVersion[1:] + } + tags, err := GetVersionTags() + if err != nil { + return false, err + } + + for _, tag := range tags { + if tag.String() == tagVersion { + return true, nil + } + } + return false, nil +} diff --git a/v3/internal/github/semver.go b/v3/internal/github/semver.go new file mode 100644 index 000000000..9062f8820 --- /dev/null +++ b/v3/internal/github/semver.go @@ -0,0 +1,106 @@ +package github + +import ( + "fmt" + + "github.com/Masterminds/semver" +) + +const majorVersion = 3 + +// SemanticVersion is a struct containing a semantic version +type SemanticVersion struct { + Version *semver.Version +} + +// NewSemanticVersion creates a new SemanticVersion object with the given version string +func NewSemanticVersion(version string) (*SemanticVersion, error) { + semverVersion, err := semver.NewVersion(version) + if err != nil { + return nil, err + } + return &SemanticVersion{ + Version: semverVersion, + }, nil +} + +// IsRelease returns true if it's a release version +func (s *SemanticVersion) IsRelease() bool { + if s.Version.Major() != majorVersion { + return false + } + return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0 +} + +// IsPreRelease returns true if it's a prerelease version +func (s *SemanticVersion) IsPreRelease() bool { + if s.Version.Major() != majorVersion { + return false + } + return len(s.Version.Prerelease()) > 0 +} + +func (s *SemanticVersion) String() string { + return s.Version.String() +} + +// IsGreaterThan returns true if this version is greater than the given version +func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) { + // Set up new constraint + constraint, err := semver.NewConstraint("> " + version.Version.String()) + if err != nil { + return false, err + } + + // Check if the desired one is greater than the requested on + success, msgs := constraint.Validate(s.Version) + if !success { + return false, msgs[0] + } + return true, nil +} + +// IsGreaterThanOrEqual returns true if this version is greater than or equal the given version +func (s *SemanticVersion) IsGreaterThanOrEqual(version *SemanticVersion) (bool, error) { + // Set up new constraint + constraint, err := semver.NewConstraint(">= " + version.Version.String()) + if err != nil { + return false, err + } + + // Check if the desired one is greater than the requested on + success, msgs := constraint.Validate(s.Version) + if !success { + return false, msgs[0] + } + return true, nil +} + +// MainVersion returns the main version of any version+prerelease+metadata +// EG: MainVersion("1.2.3-pre") => "1.2.3" +func (s *SemanticVersion) MainVersion() *SemanticVersion { + mainVersion := fmt.Sprintf("%d.%d.%d", s.Version.Major(), s.Version.Minor(), s.Version.Patch()) + result, _ := NewSemanticVersion(mainVersion) + return result +} + +// SemverCollection is a collection of SemanticVersion objects +type SemverCollection []*SemanticVersion + +// Len returns the length of a collection. The number of Version instances +// on the slice. +func (c SemverCollection) Len() int { + return len(c) +} + +// Less is needed for the sort interface to compare two Version objects on the +// slice. If checks if one is less than the other. +func (c SemverCollection) Less(i, j int) bool { + return c[i].Version.LessThan(c[j].Version) +} + +// Swap is needed for the sort interface to replace the Version objects +// at two different positions in the slice. +func (c SemverCollection) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} diff --git a/v3/internal/github/semver_test.go b/v3/internal/github/semver_test.go new file mode 100644 index 000000000..b45e1b3aa --- /dev/null +++ b/v3/internal/github/semver_test.go @@ -0,0 +1,43 @@ +package github + +import ( + "github.com/matryer/is" + "testing" +) + +func TestSemanticVersion_IsGreaterThan(t *testing.T) { + is2 := is.New(t) + + alpha1, err := NewSemanticVersion("v3.0.0-alpha.1") + is2.NoErr(err) + + beta1, err := NewSemanticVersion("v3.0.0-beta.1") + is2.NoErr(err) + + v2, err := NewSemanticVersion("v3.0.0") + is2.NoErr(err) + + is2.True(alpha1.IsPreRelease()) + is2.True(beta1.IsPreRelease()) + is2.True(!v2.IsPreRelease()) + is2.True(v2.IsRelease()) + + result, err := beta1.IsGreaterThan(alpha1) + is2.NoErr(err) + is2.True(result) + + result, err = v2.IsGreaterThan(beta1) + is2.NoErr(err) + is2.True(result) + + beta44, err := NewSemanticVersion("v2.0.0-beta.44.2") + is2.NoErr(err) + + rc1, err := NewSemanticVersion("v2.0.0-rc.1") + is2.NoErr(err) + + result, err = rc1.IsGreaterThan(beta44) + is2.NoErr(err) + is2.True(result) + +} diff --git a/v3/internal/go-common-file-dialog/LICENSE b/v3/internal/go-common-file-dialog/LICENSE new file mode 100644 index 000000000..508b6978e --- /dev/null +++ b/v3/internal/go-common-file-dialog/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Harry Phillips + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v3/internal/go-common-file-dialog/README.md b/v3/internal/go-common-file-dialog/README.md new file mode 100644 index 000000000..1cb5902d1 --- /dev/null +++ b/v3/internal/go-common-file-dialog/README.md @@ -0,0 +1,31 @@ +# Common File Dialog bindings for Golang + +[Project Home](https://github.com/harry1453/go-common-file-dialog) + +This library contains bindings for Windows Vista and +newer's [Common File Dialogs](https://docs.microsoft.com/en-us/windows/win32/shell/common-file-dialog), which is the +standard system dialog for selecting files or folders to open or save. + +The Common File Dialogs have to be accessed via +the [COM Interface](https://en.wikipedia.org/wiki/Component_Object_Model), normally via C++ or via bindings (like in C#) +. + +This library contains bindings for Golang. **It does not require CGO**, and contains empty stubs for non-windows +platforms (so is safe to compile and run on platforms other than windows, but will just return errors at runtime). + +This can be very useful if you want to quickly get a file selector in your Golang application. The `cfdutil` package +contains utility functions with a single call to open and configure a dialog, and then get the result from it. Examples +for this are in [`_examples/usingutil`](_examples/usingutil). Or, if you want finer control over the dialog's operation, +you can use the base package. Examples for this are in [`_examples/notusingutil`](_examples/notusingutil). + +This library is available under the MIT license. + +Currently supported features: + +* Open File Dialog (to open a single file) +* Open Multiple Files Dialog (to open multiple files) +* Open Folder Dialog +* Save File Dialog +* Dialog "roles" to allow Windows to remember different "last locations" for different types of dialog +* Set dialog Title, Default Folder and Initial Folder +* Set dialog File Filters diff --git a/v3/internal/go-common-file-dialog/cfd/CommonFileDialog.go b/v3/internal/go-common-file-dialog/cfd/CommonFileDialog.go new file mode 100644 index 000000000..58e97aa4e --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/CommonFileDialog.go @@ -0,0 +1,72 @@ +// Cross-platform. + +// Common File Dialogs +package cfd + +type Dialog interface { + // Show the dialog to the user. + // Blocks until the user has closed the dialog. + Show() error + // Sets the dialog's parent window. Use 0 to set the dialog to have no parent window. + SetParentWindowHandle(hwnd uintptr) + // Show the dialog to the user. + // Blocks until the user has closed the dialog and returns their selection. + // Returns an error if the user cancelled the dialog. + // Do not use for the Open Multiple Files dialog. Use ShowAndGetResults instead. + ShowAndGetResult() (string, error) + // Sets the title of the dialog window. + SetTitle(title string) error + // Sets the "role" of the dialog. This is used to derive the dialog's GUID, which the + // OS will use to differentiate it from dialogs that are intended for other purposes. + // This means that, for example, a dialog with role "Import" will have a different + // previous location that it will open to than a dialog with role "Open". Can be any string. + SetRole(role string) error + // Sets the folder used as a default if there is not a recently used folder value available + SetDefaultFolder(defaultFolder string) error + // Sets the folder that the dialog always opens to. + // If this is set, it will override the "default folder" behaviour and the dialog will always open to this folder. + SetFolder(folder string) error + // Gets the selected file or folder path, as an absolute path eg. "C:\Folder\file.txt" + // Do not use for the Open Multiple Files dialog. Use GetResults instead. + GetResult() (string, error) + // Sets the file name, I.E. the contents of the file name text box. + // For Select Folder Dialog, sets folder name. + SetFileName(fileName string) error + // Release the resources allocated to this Dialog. + // Should be called when the dialog is finished with. + Release() error +} + +type FileDialog interface { + Dialog + // Set the list of file filters that the user can select. + SetFileFilters(fileFilter []FileFilter) error + // Set the selected item from the list of file filters (set using SetFileFilters) by its index. Defaults to 0 (the first item in the list) if not called. + SetSelectedFileFilterIndex(index uint) error + // Sets the default extension applied when a user does not provide one as part of the file name. + // If the user selects a different file filter, the default extension will be automatically updated to match the new file filter. + // For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists. + // For Save File Dialog, this extension will be used whenever a user does not specify an extension. + SetDefaultExtension(defaultExtension string) error +} + +type OpenFileDialog interface { + FileDialog +} + +type OpenMultipleFilesDialog interface { + FileDialog + // Show the dialog to the user. + // Blocks until the user has closed the dialog and returns the selected files. + ShowAndGetResults() ([]string, error) + // Gets the selected file paths, as absolute paths eg. "C:\Folder\file.txt" + GetResults() ([]string, error) +} + +type SelectFolderDialog interface { + Dialog +} + +type SaveFileDialog interface { // TODO Properties + FileDialog +} diff --git a/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_nonWindows.go b/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_nonWindows.go new file mode 100644 index 000000000..3ab969850 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_nonWindows.go @@ -0,0 +1,28 @@ +//go:build !windows +// +build !windows + +package cfd + +import "fmt" + +var unsupportedError = fmt.Errorf("common file dialogs are only available on windows") + +// TODO doc +func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) { + return nil, unsupportedError +} + +// TODO doc +func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) { + return nil, unsupportedError +} + +// TODO doc +func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) { + return nil, unsupportedError +} + +// TODO doc +func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) { + return nil, unsupportedError +} diff --git a/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_windows.go b/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_windows.go new file mode 100644 index 000000000..69f46118e --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_windows.go @@ -0,0 +1,79 @@ +//go:build windows +// +build windows + +package cfd + +import "github.com/go-ole/go-ole" + +func initialize() { + // Swallow error + _ = ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE) +} + +// TODO doc +func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) { + initialize() + + openDialog, err := newIFileOpenDialog() + if err != nil { + return nil, err + } + err = config.apply(openDialog) + if err != nil { + return nil, err + } + return openDialog, nil +} + +// TODO doc +func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) { + initialize() + + openDialog, err := newIFileOpenDialog() + if err != nil { + return nil, err + } + err = config.apply(openDialog) + if err != nil { + return nil, err + } + err = openDialog.setIsMultiselect(true) + if err != nil { + return nil, err + } + return openDialog, nil +} + +// TODO doc +func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) { + initialize() + + openDialog, err := newIFileOpenDialog() + if err != nil { + return nil, err + } + err = config.apply(openDialog) + if err != nil { + return nil, err + } + err = openDialog.setPickFolders(true) + if err != nil { + return nil, err + } + return openDialog, nil +} + +// TODO doc +func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) { + initialize() + + saveDialog, err := newIFileSaveDialog() + if err != nil { + return nil, err + } + err = config.apply(saveDialog) + if err != nil { + return nil, err + } + return saveDialog, nil +} diff --git a/v3/internal/go-common-file-dialog/cfd/DialogConfig.go b/v3/internal/go-common-file-dialog/cfd/DialogConfig.go new file mode 100644 index 000000000..800573ed2 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/DialogConfig.go @@ -0,0 +1,141 @@ +// Cross-platform. + +package cfd + +import ( + "reflect" + "fmt" + "os" +) + +type FileFilter struct { + // The display name of the filter (That is shown to the user) + DisplayName string + // The filter pattern. Eg. "*.txt;*.png" to select all txt and png files, "*.*" to select any files, etc. + Pattern string +} + +// Never obfuscate the FileFilter type. +var _ = reflect.TypeOf(FileFilter{}) + +type DialogConfig struct { + // The title of the dialog + Title string + // The role of the dialog. This is used to derive the dialog's GUID, which the + // OS will use to differentiate it from dialogs that are intended for other purposes. + // This means that, for example, a dialog with role "Import" will have a different + // previous location that it will open to than a dialog with role "Open". Can be any string. + Role string + // The default folder - the folder that is used the first time the user opens it + // (after the first time their last used location is used). + DefaultFolder string + // The initial folder - the folder that the dialog always opens to if not empty. + // If this is not empty, it will override the "default folder" behaviour and + // the dialog will always open to this folder. + Folder string + // The file filters that restrict which types of files the dialog is able to choose. + // Ignored by Select Folder Dialog. + FileFilters []FileFilter + // Sets the initially selected file filter. This is an index of FileFilters. + // Ignored by Select Folder Dialog. + SelectedFileFilterIndex uint + // The initial name of the file (I.E. the text in the file name text box) when the user opens the dialog. + // For the Select Folder Dialog, this sets the initial folder name. + FileName string + // The default extension applied when a user does not provide one as part of the file name. + // If the user selects a different file filter, the default extension will be automatically updated to match the new file filter. + // For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists. + // For Save File Dialog, this extension will be used whenever a user does not specify an extension. + // Ignored by Select Folder Dialog. + DefaultExtension string + // ParentWindowHandle is the handle (HWND) to the parent window of the dialog. + // If left as 0 / nil, the dialog will have no parent window. + ParentWindowHandle uintptr +} + +var defaultFilters = []FileFilter{ + { + DisplayName: "All Files (*.*)", + Pattern: "*.*", + }, +} + +func (config *DialogConfig) apply(dialog Dialog) (err error) { + if config.Title != "" { + err = dialog.SetTitle(config.Title) + if err != nil { + return + } + } + + if config.Role != "" { + err = dialog.SetRole(config.Role) + if err != nil { + return + } + } + + if config.Folder != "" { + _, err = os.Stat(config.Folder) + if err != nil { + return + } + err = dialog.SetFolder(config.Folder) + if err != nil { + return + } + } + + if config.DefaultFolder != "" { + _, err = os.Stat(config.DefaultFolder) + if err != nil { + return + } + err = dialog.SetDefaultFolder(config.DefaultFolder) + if err != nil { + return + } + } + + if config.FileName != "" { + err = dialog.SetFileName(config.FileName) + if err != nil { + return + } + } + + dialog.SetParentWindowHandle(config.ParentWindowHandle) + + if dialog, ok := dialog.(FileDialog); ok { + var fileFilters []FileFilter + if config.FileFilters != nil && len(config.FileFilters) > 0 { + fileFilters = config.FileFilters + } else { + fileFilters = defaultFilters + } + err = dialog.SetFileFilters(fileFilters) + if err != nil { + return + } + + if config.SelectedFileFilterIndex != 0 { + if config.SelectedFileFilterIndex > uint(len(fileFilters)) { + err = fmt.Errorf("selected file filter index out of range") + return + } + err = dialog.SetSelectedFileFilterIndex(config.SelectedFileFilterIndex) + if err != nil { + return + } + } + + if config.DefaultExtension != "" { + err = dialog.SetDefaultExtension(config.DefaultExtension) + if err != nil { + return + } + } + } + + return +} diff --git a/v3/internal/go-common-file-dialog/cfd/errors.go b/v3/internal/go-common-file-dialog/cfd/errors.go new file mode 100644 index 000000000..c097c8eb2 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/errors.go @@ -0,0 +1,7 @@ +package cfd + +import "errors" + +var ( + ErrorCancelled = errors.New("cancelled by user") +) diff --git a/v3/internal/go-common-file-dialog/cfd/iFileOpenDialog.go b/v3/internal/go-common-file-dialog/cfd/iFileOpenDialog.go new file mode 100644 index 000000000..404dedc22 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/iFileOpenDialog.go @@ -0,0 +1,200 @@ +//go:build windows +// +build windows + +package cfd + +import ( + "github.com/go-ole/go-ole" + "github.com/google/uuid" + "syscall" + "unsafe" +) + +var ( + fileOpenDialogCLSID = ole.NewGUID("{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}") + fileOpenDialogIID = ole.NewGUID("{d57c7288-d4ad-4768-be02-9d969532d960}") +) + +type iFileOpenDialog struct { + vtbl *iFileOpenDialogVtbl + parentWindowHandle uintptr +} + +type iFileOpenDialogVtbl struct { + iFileDialogVtbl + + GetResults uintptr // func (ppenum **IShellItemArray) HRESULT + GetSelectedItems uintptr +} + +func newIFileOpenDialog() (*iFileOpenDialog, error) { + if unknown, err := ole.CreateInstance(fileOpenDialogCLSID, fileOpenDialogIID); err == nil { + return (*iFileOpenDialog)(unsafe.Pointer(unknown)), nil + } else { + return nil, err + } +} + +func (fileOpenDialog *iFileOpenDialog) Show() error { + return fileOpenDialog.vtbl.show(unsafe.Pointer(fileOpenDialog), fileOpenDialog.parentWindowHandle) +} + +func (fileOpenDialog *iFileOpenDialog) SetParentWindowHandle(hwnd uintptr) { + fileOpenDialog.parentWindowHandle = hwnd +} + +func (fileOpenDialog *iFileOpenDialog) ShowAndGetResult() (string, error) { + isMultiselect, err := fileOpenDialog.isMultiselect() + if err != nil { + return "", err + } + if isMultiselect { + // We should panic as this error is caused by the developer using the library + panic("use ShowAndGetResults for open multiple files dialog") + } + if err := fileOpenDialog.Show(); err != nil { + return "", err + } + return fileOpenDialog.GetResult() +} + +func (fileOpenDialog *iFileOpenDialog) ShowAndGetResults() ([]string, error) { + isMultiselect, err := fileOpenDialog.isMultiselect() + if err != nil { + return nil, err + } + if !isMultiselect { + // We should panic as this error is caused by the developer using the library + panic("use ShowAndGetResult for open single file dialog") + } + if err := fileOpenDialog.Show(); err != nil { + return nil, err + } + return fileOpenDialog.GetResults() +} + +func (fileOpenDialog *iFileOpenDialog) SetTitle(title string) error { + return fileOpenDialog.vtbl.setTitle(unsafe.Pointer(fileOpenDialog), title) +} + +func (fileOpenDialog *iFileOpenDialog) GetResult() (string, error) { + isMultiselect, err := fileOpenDialog.isMultiselect() + if err != nil { + return "", err + } + if isMultiselect { + // We should panic as this error is caused by the developer using the library + panic("use GetResults for open multiple files dialog") + } + return fileOpenDialog.vtbl.getResultString(unsafe.Pointer(fileOpenDialog)) +} + +func (fileOpenDialog *iFileOpenDialog) Release() error { + return fileOpenDialog.vtbl.release(unsafe.Pointer(fileOpenDialog)) +} + +func (fileOpenDialog *iFileOpenDialog) SetDefaultFolder(defaultFolderPath string) error { + return fileOpenDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath) +} + +func (fileOpenDialog *iFileOpenDialog) SetFolder(defaultFolderPath string) error { + return fileOpenDialog.vtbl.setFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath) +} + +func (fileOpenDialog *iFileOpenDialog) SetFileFilters(filter []FileFilter) error { + return fileOpenDialog.vtbl.setFileTypes(unsafe.Pointer(fileOpenDialog), filter) +} + +func (fileOpenDialog *iFileOpenDialog) SetRole(role string) error { + return fileOpenDialog.vtbl.setClientGuid(unsafe.Pointer(fileOpenDialog), StringToUUID(role)) +} + +// This should only be callable when the user asks for a multi select because +// otherwise they will be given the Dialog interface which does not expose this function. +func (fileOpenDialog *iFileOpenDialog) GetResults() ([]string, error) { + isMultiselect, err := fileOpenDialog.isMultiselect() + if err != nil { + return nil, err + } + if !isMultiselect { + // We should panic as this error is caused by the developer using the library + panic("use GetResult for open single file dialog") + } + return fileOpenDialog.vtbl.getResultsStrings(unsafe.Pointer(fileOpenDialog)) +} + +func (fileOpenDialog *iFileOpenDialog) SetDefaultExtension(defaultExtension string) error { + return fileOpenDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileOpenDialog), defaultExtension) +} + +func (fileOpenDialog *iFileOpenDialog) SetFileName(initialFileName string) error { + return fileOpenDialog.vtbl.setFileName(unsafe.Pointer(fileOpenDialog), initialFileName) +} + +func (fileOpenDialog *iFileOpenDialog) SetSelectedFileFilterIndex(index uint) error { + return fileOpenDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileOpenDialog), index) +} + +func (fileOpenDialog *iFileOpenDialog) setPickFolders(pickFolders bool) error { + const FosPickfolders = 0x20 + if pickFolders { + return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosPickfolders) + } else { + return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosPickfolders) + } +} + +const FosAllowMultiselect = 0x200 + +func (fileOpenDialog *iFileOpenDialog) isMultiselect() (bool, error) { + options, err := fileOpenDialog.vtbl.getOptions(unsafe.Pointer(fileOpenDialog)) + if err != nil { + return false, err + } + return options&FosAllowMultiselect != 0, nil +} + +func (fileOpenDialog *iFileOpenDialog) setIsMultiselect(isMultiselect bool) error { + if isMultiselect { + return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect) + } else { + return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect) + } +} + +func (vtbl *iFileOpenDialogVtbl) getResults(objPtr unsafe.Pointer) (*iShellItemArray, error) { + var shellItemArray *iShellItemArray + ret, _, _ := syscall.SyscallN(vtbl.GetResults, + uintptr(objPtr), + uintptr(unsafe.Pointer(&shellItemArray)), + 0) + return shellItemArray, hresultToError(ret) +} + +func (vtbl *iFileOpenDialogVtbl) getResultsStrings(objPtr unsafe.Pointer) ([]string, error) { + shellItemArray, err := vtbl.getResults(objPtr) + if err != nil { + return nil, err + } + if shellItemArray == nil { + return nil, ErrorCancelled + } + defer shellItemArray.vtbl.release(unsafe.Pointer(shellItemArray)) + count, err := shellItemArray.vtbl.getCount(unsafe.Pointer(shellItemArray)) + if err != nil { + return nil, err + } + var results []string + for i := uintptr(0); i < count; i++ { + newItem, err := shellItemArray.vtbl.getItemAt(unsafe.Pointer(shellItemArray), i) + if err != nil { + return nil, err + } + results = append(results, newItem) + } + return results, nil +} + +func StringToUUID(str string) *ole.GUID { + return ole.NewGUID(uuid.NewSHA1(uuid.Nil, []byte(str)).String()) +} diff --git a/v3/internal/go-common-file-dialog/cfd/iFileSaveDialog.go b/v3/internal/go-common-file-dialog/cfd/iFileSaveDialog.go new file mode 100644 index 000000000..ddee7b246 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/iFileSaveDialog.go @@ -0,0 +1,92 @@ +//go:build windows +// +build windows + +package cfd + +import ( + "github.com/go-ole/go-ole" + "unsafe" +) + +var ( + saveFileDialogCLSID = ole.NewGUID("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}") + saveFileDialogIID = ole.NewGUID("{84bccd23-5fde-4cdb-aea4-af64b83d78ab}") +) + +type iFileSaveDialog struct { + vtbl *iFileSaveDialogVtbl + parentWindowHandle uintptr +} + +type iFileSaveDialogVtbl struct { + iFileDialogVtbl + + SetSaveAsItem uintptr + SetProperties uintptr + SetCollectedProperties uintptr + GetProperties uintptr + ApplyProperties uintptr +} + +func newIFileSaveDialog() (*iFileSaveDialog, error) { + if unknown, err := ole.CreateInstance(saveFileDialogCLSID, saveFileDialogIID); err == nil { + return (*iFileSaveDialog)(unsafe.Pointer(unknown)), nil + } else { + return nil, err + } +} + +func (fileSaveDialog *iFileSaveDialog) Show() error { + return fileSaveDialog.vtbl.show(unsafe.Pointer(fileSaveDialog), fileSaveDialog.parentWindowHandle) +} + +func (fileSaveDialog *iFileSaveDialog) SetParentWindowHandle(hwnd uintptr) { + fileSaveDialog.parentWindowHandle = hwnd +} + +func (fileSaveDialog *iFileSaveDialog) ShowAndGetResult() (string, error) { + if err := fileSaveDialog.Show(); err != nil { + return "", err + } + return fileSaveDialog.GetResult() +} + +func (fileSaveDialog *iFileSaveDialog) SetTitle(title string) error { + return fileSaveDialog.vtbl.setTitle(unsafe.Pointer(fileSaveDialog), title) +} + +func (fileSaveDialog *iFileSaveDialog) GetResult() (string, error) { + return fileSaveDialog.vtbl.getResultString(unsafe.Pointer(fileSaveDialog)) +} + +func (fileSaveDialog *iFileSaveDialog) Release() error { + return fileSaveDialog.vtbl.release(unsafe.Pointer(fileSaveDialog)) +} + +func (fileSaveDialog *iFileSaveDialog) SetDefaultFolder(defaultFolderPath string) error { + return fileSaveDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath) +} + +func (fileSaveDialog *iFileSaveDialog) SetFolder(defaultFolderPath string) error { + return fileSaveDialog.vtbl.setFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath) +} + +func (fileSaveDialog *iFileSaveDialog) SetFileFilters(filter []FileFilter) error { + return fileSaveDialog.vtbl.setFileTypes(unsafe.Pointer(fileSaveDialog), filter) +} + +func (fileSaveDialog *iFileSaveDialog) SetRole(role string) error { + return fileSaveDialog.vtbl.setClientGuid(unsafe.Pointer(fileSaveDialog), StringToUUID(role)) +} + +func (fileSaveDialog *iFileSaveDialog) SetDefaultExtension(defaultExtension string) error { + return fileSaveDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileSaveDialog), defaultExtension) +} + +func (fileSaveDialog *iFileSaveDialog) SetFileName(initialFileName string) error { + return fileSaveDialog.vtbl.setFileName(unsafe.Pointer(fileSaveDialog), initialFileName) +} + +func (fileSaveDialog *iFileSaveDialog) SetSelectedFileFilterIndex(index uint) error { + return fileSaveDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileSaveDialog), index) +} diff --git a/v3/internal/go-common-file-dialog/cfd/iShellItem.go b/v3/internal/go-common-file-dialog/cfd/iShellItem.go new file mode 100644 index 000000000..080115345 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/iShellItem.go @@ -0,0 +1,56 @@ +//go:build windows +// +build windows + +package cfd + +import ( + "github.com/go-ole/go-ole" + "syscall" + "unsafe" +) + +var ( + procSHCreateItemFromParsingName = syscall.NewLazyDLL("Shell32.dll").NewProc("SHCreateItemFromParsingName") + iidShellItem = ole.NewGUID("43826d1e-e718-42ee-bc55-a1e261c37bfe") +) + +type iShellItem struct { + vtbl *iShellItemVtbl +} + +type iShellItemVtbl struct { + iUnknownVtbl + BindToHandler uintptr + GetParent uintptr + GetDisplayName uintptr // func (sigdnName SIGDN, ppszName *LPWSTR) HRESULT + GetAttributes uintptr + Compare uintptr +} + +func newIShellItem(path string) (*iShellItem, error) { + var shellItem *iShellItem + pathPtr := ole.SysAllocString(path) + defer func(v *int16) { + _ = ole.SysFreeString(v) + }(pathPtr) + + ret, _, _ := procSHCreateItemFromParsingName.Call( + uintptr(unsafe.Pointer(pathPtr)), + 0, + uintptr(unsafe.Pointer(iidShellItem)), + uintptr(unsafe.Pointer(&shellItem))) + return shellItem, hresultToError(ret) +} + +func (vtbl *iShellItemVtbl) getDisplayName(objPtr unsafe.Pointer) (string, error) { + var ptr *uint16 + ret, _, _ := syscall.SyscallN(vtbl.GetDisplayName, + uintptr(objPtr), + 0x80058000, // SIGDN_FILESYSPATH, + uintptr(unsafe.Pointer(&ptr))) + if err := hresultToError(ret); err != nil { + return "", err + } + defer ole.CoTaskMemFree(uintptr(unsafe.Pointer(ptr))) + return ole.LpOleStrToString(ptr), nil +} diff --git a/v3/internal/go-common-file-dialog/cfd/iShellItemArray.go b/v3/internal/go-common-file-dialog/cfd/iShellItemArray.go new file mode 100644 index 000000000..bdd459402 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/iShellItemArray.go @@ -0,0 +1,65 @@ +//go:build windows +// +build windows + +package cfd + +import ( + "fmt" + "github.com/go-ole/go-ole" + "syscall" + "unsafe" +) + +const ( + iidShellItemArrayGUID = "{b63ea76d-1f85-456f-a19c-48159efa858b}" +) + +var ( + iidShellItemArray *ole.GUID +) + +func init() { + iidShellItemArray, _ = ole.IIDFromString(iidShellItemArrayGUID) +} + +type iShellItemArray struct { + vtbl *iShellItemArrayVtbl +} + +type iShellItemArrayVtbl struct { + iUnknownVtbl + BindToHandler uintptr + GetPropertyStore uintptr + GetPropertyDescriptionList uintptr + GetAttributes uintptr + GetCount uintptr // func (pdwNumItems *DWORD) HRESULT + GetItemAt uintptr // func (dwIndex DWORD, ppsi **IShellItem) HRESULT + EnumItems uintptr +} + +func (vtbl *iShellItemArrayVtbl) getCount(objPtr unsafe.Pointer) (uintptr, error) { + var count uintptr + ret, _, _ := syscall.SyscallN(vtbl.GetCount, + uintptr(objPtr), + uintptr(unsafe.Pointer(&count))) + if err := hresultToError(ret); err != nil { + return 0, err + } + return count, nil +} + +func (vtbl *iShellItemArrayVtbl) getItemAt(objPtr unsafe.Pointer, index uintptr) (string, error) { + var shellItem *iShellItem + ret, _, _ := syscall.SyscallN(vtbl.GetItemAt, + uintptr(objPtr), + index, + uintptr(unsafe.Pointer(&shellItem))) + if err := hresultToError(ret); err != nil { + return "", err + } + if shellItem == nil { + return "", fmt.Errorf("shellItem is nil") + } + defer shellItem.vtbl.release(unsafe.Pointer(shellItem)) + return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem)) +} diff --git a/v3/internal/go-common-file-dialog/cfd/vtblCommon.go b/v3/internal/go-common-file-dialog/cfd/vtblCommon.go new file mode 100644 index 000000000..21015c27c --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/vtblCommon.go @@ -0,0 +1,48 @@ +//go:build windows +// +build windows + +package cfd + +type comDlgFilterSpec struct { + pszName *int16 + pszSpec *int16 +} + +type iUnknownVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr +} + +type iModalWindowVtbl struct { + iUnknownVtbl + Show uintptr // func (hwndOwner HWND) HRESULT +} + +type iFileDialogVtbl struct { + iModalWindowVtbl + SetFileTypes uintptr // func (cFileTypes UINT, rgFilterSpec *COMDLG_FILTERSPEC) HRESULT + SetFileTypeIndex uintptr // func(iFileType UINT) HRESULT + GetFileTypeIndex uintptr + Advise uintptr + Unadvise uintptr + SetOptions uintptr // func (fos FILEOPENDIALOGOPTIONS) HRESULT + GetOptions uintptr // func (pfos *FILEOPENDIALOGOPTIONS) HRESULT + SetDefaultFolder uintptr // func (psi *IShellItem) HRESULT + SetFolder uintptr // func (psi *IShellItem) HRESULT + GetFolder uintptr + GetCurrentSelection uintptr + SetFileName uintptr // func (pszName LPCWSTR) HRESULT + GetFileName uintptr + SetTitle uintptr // func(pszTitle LPCWSTR) HRESULT + SetOkButtonLabel uintptr + SetFileNameLabel uintptr + GetResult uintptr // func (ppsi **IShellItem) HRESULT + AddPlace uintptr + SetDefaultExtension uintptr // func (pszDefaultExtension LPCWSTR) HRESULT + // This can only be used from a callback. + Close uintptr + SetClientGuid uintptr // func (guid REFGUID) HRESULT + ClearClientData uintptr + SetFilter uintptr +} diff --git a/v3/internal/go-common-file-dialog/cfd/vtblCommonFunc.go b/v3/internal/go-common-file-dialog/cfd/vtblCommonFunc.go new file mode 100644 index 000000000..a59ff1ed1 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfd/vtblCommonFunc.go @@ -0,0 +1,226 @@ +//go:build windows + +package cfd + +import ( + "fmt" + "strings" + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +func hresultToError(hr uintptr) error { + if hr < 0 { + return ole.NewError(hr) + } + return nil +} + +func (vtbl *iUnknownVtbl) release(objPtr unsafe.Pointer) error { + ret, _, _ := syscall.SyscallN(vtbl.Release, + uintptr(objPtr), + 0) + return hresultToError(ret) +} + +func (vtbl *iModalWindowVtbl) show(objPtr unsafe.Pointer, hwnd uintptr) error { + ret, _, _ := syscall.SyscallN(vtbl.Show, + uintptr(objPtr), + hwnd) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) setFileTypes(objPtr unsafe.Pointer, filters []FileFilter) error { + cFileTypes := len(filters) + if cFileTypes < 0 { + return fmt.Errorf("must specify at least one filter") + } + comDlgFilterSpecs := make([]comDlgFilterSpec, cFileTypes) + for i := 0; i < cFileTypes; i++ { + filter := &filters[i] + comDlgFilterSpecs[i] = comDlgFilterSpec{ + pszName: ole.SysAllocString(filter.DisplayName), + pszSpec: ole.SysAllocString(filter.Pattern), + } + } + + // Ensure memory is freed after use + defer func() { + for _, spec := range comDlgFilterSpecs { + ole.SysFreeString(spec.pszName) + ole.SysFreeString(spec.pszSpec) + } + }() + + ret, _, _ := syscall.SyscallN(vtbl.SetFileTypes, + uintptr(objPtr), + uintptr(cFileTypes), + uintptr(unsafe.Pointer(&comDlgFilterSpecs[0]))) + return hresultToError(ret) +} + +// Options are: +// FOS_OVERWRITEPROMPT = 0x2, +// FOS_STRICTFILETYPES = 0x4, +// FOS_NOCHANGEDIR = 0x8, +// FOS_PICKFOLDERS = 0x20, +// FOS_FORCEFILESYSTEM = 0x40, +// FOS_ALLNONSTORAGEITEMS = 0x80, +// FOS_NOVALIDATE = 0x100, +// FOS_ALLOWMULTISELECT = 0x200, +// FOS_PATHMUSTEXIST = 0x800, +// FOS_FILEMUSTEXIST = 0x1000, +// FOS_CREATEPROMPT = 0x2000, +// FOS_SHAREAWARE = 0x4000, +// FOS_NOREADONLYRETURN = 0x8000, +// FOS_NOTESTFILECREATE = 0x10000, +// FOS_HIDEMRUPLACES = 0x20000, +// FOS_HIDEPINNEDPLACES = 0x40000, +// FOS_NODEREFERENCELINKS = 0x100000, +// FOS_OKBUTTONNEEDSINTERACTION = 0x200000, +// FOS_DONTADDTORECENT = 0x2000000, +// FOS_FORCESHOWHIDDEN = 0x10000000, +// FOS_DEFAULTNOMINIMODE = 0x20000000, +// FOS_FORCEPREVIEWPANEON = 0x40000000, +// FOS_SUPPORTSTREAMABLEITEMS = 0x80000000 +func (vtbl *iFileDialogVtbl) setOptions(objPtr unsafe.Pointer, options uint32) error { + ret, _, _ := syscall.SyscallN(vtbl.SetOptions, + uintptr(objPtr), + uintptr(options)) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) getOptions(objPtr unsafe.Pointer) (uint32, error) { + var options uint32 + ret, _, _ := syscall.SyscallN(vtbl.GetOptions, + uintptr(objPtr), + uintptr(unsafe.Pointer(&options))) + return options, hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) addOption(objPtr unsafe.Pointer, option uint32) error { + if options, err := vtbl.getOptions(objPtr); err == nil { + return vtbl.setOptions(objPtr, options|option) + } else { + return err + } +} + +func (vtbl *iFileDialogVtbl) removeOption(objPtr unsafe.Pointer, option uint32) error { + if options, err := vtbl.getOptions(objPtr); err == nil { + return vtbl.setOptions(objPtr, options&^option) + } else { + return err + } +} + +func (vtbl *iFileDialogVtbl) setDefaultFolder(objPtr unsafe.Pointer, path string) error { + shellItem, err := newIShellItem(path) + if err != nil { + return err + } + defer shellItem.vtbl.release(unsafe.Pointer(shellItem)) + ret, _, _ := syscall.SyscallN(vtbl.SetDefaultFolder, + uintptr(objPtr), + uintptr(unsafe.Pointer(shellItem))) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) setFolder(objPtr unsafe.Pointer, path string) error { + shellItem, err := newIShellItem(path) + if err != nil { + return err + } + defer shellItem.vtbl.release(unsafe.Pointer(shellItem)) + ret, _, _ := syscall.SyscallN(vtbl.SetFolder, + uintptr(objPtr), + uintptr(unsafe.Pointer(shellItem))) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) setTitle(objPtr unsafe.Pointer, title string) error { + titlePtr := ole.SysAllocString(title) + defer ole.SysFreeString(titlePtr) // Ensure the string is freed + ret, _, _ := syscall.SyscallN(vtbl.SetTitle, + uintptr(objPtr), + uintptr(unsafe.Pointer(titlePtr))) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) close(objPtr unsafe.Pointer) error { + ret, _, _ := syscall.SyscallN(vtbl.Close, + uintptr(objPtr)) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) getResult(objPtr unsafe.Pointer) (*iShellItem, error) { + var shellItem *iShellItem + ret, _, _ := syscall.SyscallN(vtbl.GetResult, + uintptr(objPtr), + uintptr(unsafe.Pointer(&shellItem))) + return shellItem, hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) getResultString(objPtr unsafe.Pointer) (string, error) { + shellItem, err := vtbl.getResult(objPtr) + if err != nil { + return "", err + } + if shellItem == nil { + return "", ErrorCancelled + } + defer shellItem.vtbl.release(unsafe.Pointer(shellItem)) + return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem)) +} + +func (vtbl *iFileDialogVtbl) setClientGuid(objPtr unsafe.Pointer, guid *ole.GUID) error { + // Ensure the GUID is not nil + if guid == nil { + return fmt.Errorf("guid cannot be nil") + } + + // Call the SetClientGuid method + ret, _, _ := syscall.SyscallN(vtbl.SetClientGuid, + uintptr(objPtr), + uintptr(unsafe.Pointer(guid))) + + // Convert the HRESULT to a Go error + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) setDefaultExtension(objPtr unsafe.Pointer, defaultExtension string) error { + // Ensure the string is not empty before accessing the first character + if len(defaultExtension) > 0 && defaultExtension[0] == '.' { + defaultExtension = strings.TrimPrefix(defaultExtension, ".") + } + + // Allocate memory for the default extension string + defaultExtensionPtr := ole.SysAllocString(defaultExtension) + defer ole.SysFreeString(defaultExtensionPtr) // Ensure the string is freed + + // Call the SetDefaultExtension method + ret, _, _ := syscall.SyscallN(vtbl.SetDefaultExtension, + uintptr(objPtr), + uintptr(unsafe.Pointer(defaultExtensionPtr))) + + // Convert the HRESULT to a Go error + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) setFileName(objPtr unsafe.Pointer, fileName string) error { + fileNamePtr := ole.SysAllocString(fileName) + defer ole.SysFreeString(fileNamePtr) // Ensure the string is freed + ret, _, _ := syscall.SyscallN(vtbl.SetFileName, + uintptr(objPtr), + uintptr(unsafe.Pointer(fileNamePtr))) + return hresultToError(ret) +} + +func (vtbl *iFileDialogVtbl) setSelectedFileFilterIndex(objPtr unsafe.Pointer, index uint) error { + ret, _, _ := syscall.SyscallN(vtbl.SetFileTypeIndex, + uintptr(objPtr), + uintptr(index+1)) // SetFileTypeIndex counts from 1 + return hresultToError(ret) +} diff --git a/v3/internal/go-common-file-dialog/cfdutil/CFDUtil.go b/v3/internal/go-common-file-dialog/cfdutil/CFDUtil.go new file mode 100644 index 000000000..aa3a783b2 --- /dev/null +++ b/v3/internal/go-common-file-dialog/cfdutil/CFDUtil.go @@ -0,0 +1,45 @@ +package cfdutil + +import ( + "github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd" +) + +// TODO doc +func ShowOpenFileDialog(config cfd.DialogConfig) (string, error) { + dialog, err := cfd.NewOpenFileDialog(config) + if err != nil { + return "", err + } + defer dialog.Release() + return dialog.ShowAndGetResult() +} + +// TODO doc +func ShowOpenMultipleFilesDialog(config cfd.DialogConfig) ([]string, error) { + dialog, err := cfd.NewOpenMultipleFilesDialog(config) + if err != nil { + return nil, err + } + defer dialog.Release() + return dialog.ShowAndGetResults() +} + +// TODO doc +func ShowPickFolderDialog(config cfd.DialogConfig) (string, error) { + dialog, err := cfd.NewSelectFolderDialog(config) + if err != nil { + return "", err + } + defer dialog.Release() + return dialog.ShowAndGetResult() +} + +// TODO doc +func ShowSaveFileDialog(config cfd.DialogConfig) (string, error) { + dialog, err := cfd.NewSaveFileDialog(config) + if err != nil { + return "", err + } + defer dialog.Release() + return dialog.ShowAndGetResult() +} diff --git a/v3/internal/go-common-file-dialog/util/util.go b/v3/internal/go-common-file-dialog/util/util.go new file mode 100644 index 000000000..723fbedc0 --- /dev/null +++ b/v3/internal/go-common-file-dialog/util/util.go @@ -0,0 +1,10 @@ +package util + +import ( + "github.com/go-ole/go-ole" + "github.com/google/uuid" +) + +func StringToUUID(str string) *ole.GUID { + return ole.NewGUID(uuid.NewSHA1(uuid.Nil, []byte(str)).String()) +} diff --git a/v3/internal/go-common-file-dialog/util/util_test.go b/v3/internal/go-common-file-dialog/util/util_test.go new file mode 100644 index 000000000..2e8ffeb05 --- /dev/null +++ b/v3/internal/go-common-file-dialog/util/util_test.go @@ -0,0 +1,14 @@ +package util + +import ( + "github.com/go-ole/go-ole" + "testing" +) + +func TestStringToUUID(t *testing.T) { + generated := *StringToUUID("TestTestTest") + expected := *ole.NewGUID("7933985F-2C87-5A5B-A26E-5D0326829AC2") + if generated != expected { + t.Errorf("not equal. expected %s, found %s", expected.String(), generated.String()) + } +} diff --git a/v3/internal/hash/fnv.go b/v3/internal/hash/fnv.go new file mode 100644 index 000000000..bc18ee817 --- /dev/null +++ b/v3/internal/hash/fnv.go @@ -0,0 +1,9 @@ +package hash + +import "hash/fnv" + +func Fnv(s string) uint32 { + h := fnv.New32a() + _, _ = h.Write([]byte(s)) // Hash implementations never return errors (see https://pkg.go.dev/hash#Hash) + return h.Sum32() +} diff --git a/v3/internal/operatingsystem/os.go b/v3/internal/operatingsystem/os.go new file mode 100644 index 000000000..2d5656281 --- /dev/null +++ b/v3/internal/operatingsystem/os.go @@ -0,0 +1,23 @@ +package operatingsystem + +// OS contains information about the operating system +type OS struct { + ID string + Name string + Version string + Branding string +} + +func (o *OS) AsLogSlice() []any { + return []any{ + "ID", o.ID, + "Name", o.Name, + "Version", o.Version, + "Branding", o.Branding, + } +} + +// Info retrieves information about the current platform +func Info() (*OS, error) { + return platformInfo() +} diff --git a/v3/internal/operatingsystem/os_darwin.go b/v3/internal/operatingsystem/os_darwin.go new file mode 100644 index 000000000..2975c76f1 --- /dev/null +++ b/v3/internal/operatingsystem/os_darwin.go @@ -0,0 +1,72 @@ +//go:build darwin + +package operatingsystem + +import ( + "os/exec" + "strings" +) + +var macOSNames = map[string]string{ + "10.10": "Yosemite", + "10.11": "El Capitan", + "10.12": "Sierra", + "10.13": "High Sierra", + "10.14": "Mojave", + "10.15": "Catalina", + "11": "Big Sur", + "12": "Monterey", + "13": "Ventura", + "14": "Sonoma", + "15": "Sequoia", + // Add newer versions as they are released... +} + +func getOSName(version string) string { + trimmedVersion := version + if !strings.HasPrefix(version, "10.") { + trimmedVersion = strings.SplitN(version, ".", 2)[0] + } + name, ok := macOSNames[trimmedVersion] + if ok { + return name + } + return "MacOS " + version +} + +func getSysctlValue(key string) (string, error) { + // Run "sysctl" command + command := exec.Command("sysctl", key) + // Capture stdout + var stdout strings.Builder + command.Stdout = &stdout + // Run command + err := command.Run() + if err != nil { + return "", err + } + version := strings.TrimPrefix(stdout.String(), key+": ") + return strings.TrimSpace(version), nil +} + +func platformInfo() (*OS, error) { + // Default value + var result OS + result.ID = "Unknown" + result.Name = "MacOS" + result.Version = "Unknown" + + version, err := getSysctlValue("kern.osproductversion") + if err != nil { + return nil, err + } + result.Version = version + ID, err := getSysctlValue("kern.osversion") + if err != nil { + return nil, err + } + result.ID = ID + result.Branding = getOSName(result.Version) + + return &result, nil +} diff --git a/v3/internal/operatingsystem/os_linux.go b/v3/internal/operatingsystem/os_linux.go new file mode 100644 index 000000000..715207dc5 --- /dev/null +++ b/v3/internal/operatingsystem/os_linux.go @@ -0,0 +1,53 @@ +//go:build linux +// +build linux + +package operatingsystem + +import ( + "fmt" + "os" + "strings" +) + +// platformInfo is the platform specific method to get system information +func platformInfo() (*OS, error) { + _, err := os.Stat("/etc/os-release") + if os.IsNotExist(err) { + return nil, fmt.Errorf("unable to read system information") + } + + osRelease, _ := os.ReadFile("/etc/os-release") + return parseOsRelease(string(osRelease)), nil +} + +func parseOsRelease(osRelease string) *OS { + + // Default value + var result OS + result.ID = "Unknown" + result.Name = "Unknown" + result.Version = "Unknown" + + // Split into lines + lines := strings.Split(osRelease, "\n") + // Iterate lines + for _, line := range lines { + // Split each line by the equals char + splitLine := strings.SplitN(line, "=", 2) + // Check we have + if len(splitLine) != 2 { + continue + } + switch splitLine[0] { + case "ID": + result.ID = strings.ToLower(strings.Trim(splitLine[1], `"`)) + case "NAME": + result.Name = strings.Trim(splitLine[1], `"`) + case "VERSION_ID": + result.Version = strings.Trim(splitLine[1], `"`) + case "VERSION": + result.Branding = strings.Trim(splitLine[1], `"`) + } + } + return &result +} diff --git a/v3/internal/operatingsystem/os_windows.go b/v3/internal/operatingsystem/os_windows.go new file mode 100644 index 000000000..2e8c0775b --- /dev/null +++ b/v3/internal/operatingsystem/os_windows.go @@ -0,0 +1,34 @@ +//go:build windows + +package operatingsystem + +import ( + "fmt" + "github.com/wailsapp/wails/v3/pkg/w32" + + "golang.org/x/sys/windows/registry" +) + +func platformInfo() (*OS, error) { + // Default value + var result OS + result.ID = "Unknown" + result.Name = "Windows" + result.Version = "Unknown" + + // Credit: https://stackoverflow.com/a/33288328 + // Ignore errors as it isn't a showstopper + key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + + productName, _, _ := key.GetStringValue("ProductName") + currentBuild, _, _ := key.GetStringValue("CurrentBuildNumber") + displayVersion, _, _ := key.GetStringValue("DisplayVersion") + releaseId, _, _ := key.GetStringValue("ReleaseId") + + result.Name = productName + result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild) + result.ID = displayVersion + result.Branding = w32.GetBranding() + + return &result, key.Close() +} diff --git a/v3/internal/operatingsystem/version_windows.go b/v3/internal/operatingsystem/version_windows.go new file mode 100644 index 000000000..a8f53d134 --- /dev/null +++ b/v3/internal/operatingsystem/version_windows.go @@ -0,0 +1,62 @@ +//go:build windows + +package operatingsystem + +import ( + "strconv" + + "golang.org/x/sys/windows/registry" +) + +type WindowsVersionInfo struct { + Major int + Minor int + Build int + DisplayVersion string +} + +func (w *WindowsVersionInfo) IsWindowsVersionAtLeast(major, minor, buildNumber int) bool { + return w.Major >= major && w.Minor >= minor && w.Build >= buildNumber +} + +func GetWindowsVersionInfo() (*WindowsVersionInfo, error) { + key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + if err != nil { + return nil, err + } + + return &WindowsVersionInfo{ + Major: regDWORDKeyAsInt(key, "CurrentMajorVersionNumber"), + Minor: regDWORDKeyAsInt(key, "CurrentMinorVersionNumber"), + Build: regStringKeyAsInt(key, "CurrentBuildNumber"), + DisplayVersion: regKeyAsString(key, "DisplayVersion"), + }, nil +} + +func regDWORDKeyAsInt(key registry.Key, name string) int { + result, _, err := key.GetIntegerValue(name) + if err != nil { + return -1 + } + return int(result) +} + +func regStringKeyAsInt(key registry.Key, name string) int { + resultStr, _, err := key.GetStringValue(name) + if err != nil { + return -1 + } + result, err := strconv.Atoi(resultStr) + if err != nil { + return -1 + } + return result +} + +func regKeyAsString(key registry.Key, name string) string { + resultStr, _, err := key.GetStringValue(name) + if err != nil { + return "" + } + return resultStr +} diff --git a/v3/internal/operatingsystem/webkit_linux.go b/v3/internal/operatingsystem/webkit_linux.go new file mode 100644 index 000000000..96fcb6c1a --- /dev/null +++ b/v3/internal/operatingsystem/webkit_linux.go @@ -0,0 +1,42 @@ +//go:build linux + +package operatingsystem + +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 +#include +*/ +import "C" +import "fmt" + +type WebkitVersion struct { + Major uint + Minor uint + Micro uint +} + +func GetWebkitVersion() WebkitVersion { + var major, minor, micro C.uint + major = C.webkit_get_major_version() + minor = C.webkit_get_minor_version() + micro = C.webkit_get_micro_version() + return WebkitVersion{ + Major: uint(major), + Minor: uint(minor), + Micro: uint(micro), + } +} + +func (v WebkitVersion) String() string { + return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Micro) +} + +func (v WebkitVersion) IsAtLeast(major int, minor int, micro int) bool { + if v.Major != uint(major) { + return v.Major > uint(major) + } + if v.Minor != uint(minor) { + return v.Minor > uint(minor) + } + return v.Micro >= uint(micro) +} diff --git a/v3/internal/packager/packager.go b/v3/internal/packager/packager.go new file mode 100644 index 000000000..5cf235d72 --- /dev/null +++ b/v3/internal/packager/packager.go @@ -0,0 +1,94 @@ +// Package packager provides a simplified interface for creating Linux packages using nfpm +package packager + +import ( + "fmt" + "io" + "os" + + "github.com/goreleaser/nfpm/v2" + _ "github.com/goreleaser/nfpm/v2/apk" // Register APK packager + _ "github.com/goreleaser/nfpm/v2/arch" // Register Arch Linux packager + _ "github.com/goreleaser/nfpm/v2/deb" // Register DEB packager + _ "github.com/goreleaser/nfpm/v2/ipk" // Register IPK packager + _ "github.com/goreleaser/nfpm/v2/rpm" // Register RPM packager +) + +// PackageType represents supported package formats +type PackageType string + +const ( + // DEB is for Debian/Ubuntu packages + DEB PackageType = "deb" + // RPM is for RedHat/CentOS packages + RPM PackageType = "rpm" + // APK is for Alpine Linux packages + APK PackageType = "apk" + // IPK is for OpenWrt packages + IPK PackageType = "ipk" + // ARCH is for Arch Linux packages + ARCH PackageType = "archlinux" +) + +// CreatePackageFromConfig loads a configuration file and creates a package +func CreatePackageFromConfig(pkgType PackageType, configPath string, output string) error { + // Parse nfpm config + config, err := nfpm.ParseFile(configPath) + if err != nil { + return fmt.Errorf("error parsing config file: %w", err) + } + + // Get info for the specified packager + info, err := config.Get(string(pkgType)) + if err != nil { + return fmt.Errorf("error getting packager info: %w", err) + } + + // Get the packager + packager, err := nfpm.Get(string(pkgType)) + if err != nil { + return fmt.Errorf("error getting packager: %w", err) + } + + // Create output file + out, err := os.Create(output) + if err != nil { + return fmt.Errorf("error creating output file: %w", err) + } + defer out.Close() + + // Create the package + if err := packager.Package(info, out); err != nil { + return fmt.Errorf("error creating package: %w", err) + } + + return nil +} + +// CreatePackageFromConfigWriter loads a configuration file and writes the package to the provided writer +func CreatePackageFromConfigWriter(pkgType PackageType, configPath string, output io.Writer) error { + // Parse nfpm config + config, err := nfpm.ParseFile(configPath) + if err != nil { + return fmt.Errorf("error parsing config file: %w", err) + } + + // Get info for the specified packager + info, err := config.Get(string(pkgType)) + if err != nil { + return fmt.Errorf("error getting packager info: %w", err) + } + + // Get the packager + packager, err := nfpm.Get(string(pkgType)) + if err != nil { + return fmt.Errorf("error getting packager: %w", err) + } + + // Create the package + if err := packager.Package(info, output); err != nil { + return fmt.Errorf("error creating package: %w", err) + } + + return nil +} diff --git a/v3/internal/packager/packager_test.go b/v3/internal/packager/packager_test.go new file mode 100644 index 000000000..3ed4a4f48 --- /dev/null +++ b/v3/internal/packager/packager_test.go @@ -0,0 +1,100 @@ +package packager + +import ( + "bytes" + "os" + "path/filepath" + "testing" +) + +func TestCreatePackageFromConfig(t *testing.T) { + // Create a temporary file for testing + content := []byte("test content") + tmpfile, err := os.CreateTemp("", "example") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile.Name()) + + if _, err := tmpfile.Write(content); err != nil { + t.Fatal(err) + } + if err := tmpfile.Close(); err != nil { + t.Fatal(err) + } + + // Create a temporary config file + configContent := []byte(` +name: test-package +version: v1.0.0 +arch: amd64 +description: Test package +maintainer: Test User +license: MIT +contents: +- src: ` + tmpfile.Name() + ` + dst: /usr/local/bin/test-file +`) + + configFile, err := os.CreateTemp("", "config*.yaml") + if err != nil { + t.Fatal(err) + } + defer os.Remove(configFile.Name()) + + if _, err := configFile.Write(configContent); err != nil { + t.Fatal(err) + } + if err := configFile.Close(); err != nil { + t.Fatal(err) + } + + // Test creating packages for each format + formats := []struct { + pkgType PackageType + ext string + }{ + {DEB, "deb"}, + {RPM, "rpm"}, + {APK, "apk"}, + {IPK, "ipk"}, + {ARCH, "pkg.tar.zst"}, + } + + for _, format := range formats { + t.Run(string(format.pkgType), func(t *testing.T) { + // Test file-based package creation + outputPath := filepath.Join(os.TempDir(), "test-package."+format.ext) + err := CreatePackageFromConfig(format.pkgType, configFile.Name(), outputPath) + if err != nil { + t.Errorf("CreatePackageFromConfig failed for %s: %v", format.pkgType, err) + } + defer os.Remove(outputPath) + + // Verify the file was created + if _, err := os.Stat(outputPath); os.IsNotExist(err) { + t.Errorf("Package file was not created for %s", format.pkgType) + } + + // Test writer-based package creation + var buf bytes.Buffer + err = CreatePackageFromConfigWriter(format.pkgType, configFile.Name(), &buf) + if err != nil { + t.Errorf("CreatePackageFromConfigWriter failed for %s: %v", format.pkgType, err) + } + + // Verify some content was written + if buf.Len() == 0 { + t.Errorf("No content was written for %s", format.pkgType) + } + }) + } + + // Test with invalid config file + t.Run("InvalidConfig", func(t *testing.T) { + err := CreatePackageFromConfig(DEB, "nonexistent.yaml", "output.deb") + if err == nil { + t.Error("Expected error for invalid config, got nil") + } + }) +} diff --git a/v3/internal/runtime/.gitignore b/v3/internal/runtime/.gitignore new file mode 100644 index 000000000..059d2134d --- /dev/null +++ b/v3/internal/runtime/.gitignore @@ -0,0 +1,5 @@ +node_modules +.task +*.tsbuildinfo +desktop/@wailsio/runtime/dist/ +desktop/@wailsio/runtime/types/ diff --git a/v3/internal/runtime/README.md b/v3/internal/runtime/README.md new file mode 100644 index 000000000..c4b43d430 --- /dev/null +++ b/v3/internal/runtime/README.md @@ -0,0 +1,3 @@ +# Runtime + +To rebuild the runtime run `task build` or if you have Wails v3 CLI, you can use `wails3 task build`. diff --git a/v3/internal/runtime/Taskfile.yaml b/v3/internal/runtime/Taskfile.yaml new file mode 100644 index 000000000..2bc433213 --- /dev/null +++ b/v3/internal/runtime/Taskfile.yaml @@ -0,0 +1,57 @@ +# https://taskfile.dev + +version: "3" + +tasks: + install-deps: + internal: true + dir: desktop/@wailsio/runtime + sources: + - package.json + cmds: + - npm install + + check: + dir: desktop/@wailsio/runtime + deps: + - install-deps + cmds: + - npm run check + + test: + dir: desktop/@wailsio/runtime + deps: + - install-deps + cmds: + - npm test + + build:debug: + internal: true + cmds: + - npx esbuild@latest desktop/@wailsio/runtime/src/index.ts --inject:desktop/compiled/main.js --format=esm --target=safari11 --bundle --ignore-annotations --tree-shaking=true --sourcemap=inline --outfile=../assetserver/bundledassets/runtime.debug.js --define:DEBUG=true + + build:production: + internal: true + cmds: + - npx esbuild@latest desktop/@wailsio/runtime/src/index.ts --inject:desktop/compiled/main.js --format=esm --target=safari11 --bundle --ignore-annotations --tree-shaking=true --minify --outfile=../assetserver/bundledassets/runtime.js --define:DEBUG=false --drop:console + + build:all: + internal: true + deps: + - build:debug + - build:production + + cmds: + - cmd: echo "Build Complete." + + build: + deps: + - install-deps + cmds: + - task: build:all + + generate:events: + dir: ../../tasks/events + cmds: + - go run generate.go + - go fmt ../../pkg/events/events.go diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/.nojekyll b/v3/internal/runtime/desktop/@wailsio/runtime/docs/.nojekyll new file mode 100644 index 000000000..e2ac6616a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/hierarchy.js b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/hierarchy.js new file mode 100644 index 000000000..976e457d2 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/hierarchy.js @@ -0,0 +1 @@ +window.hierarchyData = "eJylk1FPwjAUhf/LfS7I2m3A3kSNLxiM+GIMIXW7k4auM13nC9l/Nx2RdIaZDp66tLnnfuee3QPosjQVJO8RYySgASXBJI7JLKQkYPNwQ0BjLjE1olQVJAeIGLOH4gVCAndcpSgl/5D4rMtCVLgUewQCe6EySGgUE6i1hASEMqhznmJ1c75ovDOFBAKp5FUFCZgqG1mV0anSPu6EzDQqCxzFm4ZAFMX/8pxYAjr7ZWlbnAXphTheNARmIXX6+Zvett+Ky+34etNzNnUgnjATfF3WOh0A4RR5QDQE7H/hNH34RmVeuf5E49/UKRrqfM6mJAhoa9+eDsrRxaLOc9T+LG6V5wToxG17m2Wtn6WoDCrUq6/jkngT9Aj4wgT0bxwXk1yI0QnITqcNh3Y28gV5ZhdsbTTyYvG2WtibIUH1KfjOacp6ce4x57U01xF1RDyh2DzshXq0IYj0OqiOyPAoo5jYwW2apvkBsfcRfA==" \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/highlight.css b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/highlight.css new file mode 100644 index 000000000..878117697 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/highlight.css @@ -0,0 +1,64 @@ +:root { + --light-hl-0: #0000FF; + --dark-hl-0: #569CD6; + --light-hl-1: #000000; + --dark-hl-1: #D4D4D4; + --light-hl-2: #001080; + --dark-hl-2: #9CDCFE; + --light-hl-3: #795E26; + --dark-hl-3: #DCDCAA; + --light-hl-4: #008000; + --dark-hl-4: #6A9955; + --light-hl-5: #AF00DB; + --dark-hl-5: #C586C0; + --light-code-background: #FFFFFF; + --dark-code-background: #1E1E1E; +} + +@media (prefers-color-scheme: light) { :root { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --code-background: var(--light-code-background); +} } + +@media (prefers-color-scheme: dark) { :root { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --code-background: var(--dark-code-background); +} } + +:root[data-theme='light'] { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --code-background: var(--light-code-background); +} + +:root[data-theme='dark'] { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --code-background: var(--dark-code-background); +} + +.hl-0 { color: var(--hl-0); } +.hl-1 { color: var(--hl-1); } +.hl-2 { color: var(--hl-2); } +.hl-3 { color: var(--hl-3); } +.hl-4 { color: var(--hl-4); } +.hl-5 { color: var(--hl-5); } +pre, code { background: var(--code-background); } diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/icons.js b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/icons.js new file mode 100644 index 000000000..58882d76d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/icons.js @@ -0,0 +1,18 @@ +(function() { + addIcons(); + function addIcons() { + if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); + const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); + svg.innerHTML = `MMNEPVFCICPMFPCPTTAAATR`; + svg.style.display = "none"; + if (location.protocol === "file:") updateUseElements(); + } + + function updateUseElements() { + document.querySelectorAll("use").forEach(el => { + if (el.getAttribute("href").includes("#icon-")) { + el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); + } + }); + } +})() \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/icons.svg b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/icons.svg new file mode 100644 index 000000000..50ad5799d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/icons.svg @@ -0,0 +1 @@ +MMNEPVFCICPMFPCPTTAAATR \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/main.js b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/main.js new file mode 100644 index 000000000..2363f64c2 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/main.js @@ -0,0 +1,60 @@ +"use strict"; +window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This member is normally hidden due to your filter settings.","hierarchy_expand":"Expand","hierarchy_collapse":"Collapse","folder":"Folder","kind_1":"Project","kind_2":"Module","kind_4":"Namespace","kind_8":"Enumeration","kind_16":"Enumeration Member","kind_32":"Variable","kind_64":"Function","kind_128":"Class","kind_256":"Interface","kind_512":"Constructor","kind_1024":"Property","kind_2048":"Method","kind_4096":"Call Signature","kind_8192":"Index Signature","kind_16384":"Constructor Signature","kind_32768":"Parameter","kind_65536":"Type Literal","kind_131072":"Type Parameter","kind_262144":"Accessor","kind_524288":"Get Signature","kind_1048576":"Set Signature","kind_2097152":"Type Alias","kind_4194304":"Reference","kind_8388608":"Document"}; +"use strict";(()=>{var De=Object.create;var le=Object.defineProperty;var Fe=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var qe=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var je=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Be.call(t,i)&&i!==n&&le(t,i,{get:()=>e[i],enumerable:!(r=Fe(e,i))||r.enumerable});return t};var $e=(t,e,n)=>(n=t!=null?De(Ve(t)):{},je(e||!t||!t.__esModule?le(n,"default",{value:t,enumerable:!0}):n,t));var pe=qe((de,he)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,c],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[c+1]*i[d+1],c+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),m=s.str.charAt(1),p;m in s.node.edges?p=s.node.edges[m]:(p=new t.TokenSet,s.node.edges[m]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof de=="object"?he.exports=n():e.lunr=n()}(this,function(){return t})})()});window.translations||={copy:"Copy",copied:"Copied!",normally_hidden:"This member is normally hidden due to your filter settings.",hierarchy_expand:"Expand",hierarchy_collapse:"Collapse",folder:"Folder",kind_1:"Project",kind_2:"Module",kind_4:"Namespace",kind_8:"Enumeration",kind_16:"Enumeration Member",kind_32:"Variable",kind_64:"Function",kind_128:"Class",kind_256:"Interface",kind_512:"Constructor",kind_1024:"Property",kind_2048:"Method",kind_4096:"Call Signature",kind_8192:"Index Signature",kind_16384:"Constructor Signature",kind_32768:"Parameter",kind_65536:"Type Literal",kind_131072:"Type Parameter",kind_262144:"Accessor",kind_524288:"Get Signature",kind_1048576:"Set Signature",kind_2097152:"Type Alias",kind_4194304:"Reference",kind_8388608:"Document"};var ce=[];function G(t,e){ce.push({selector:e,constructor:t})}var J=class{alwaysVisibleMember=null;constructor(){this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){ce.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!ze(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function ze(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var ue=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var ge=$e(pe(),1);async function A(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0)),r=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate")),i=await new Response(r).text();return JSON.parse(i)}async function fe(t,e){if(!window.searchData)return;let n=await A(window.searchData);t.data=n,t.index=ge.Index.load(n.index),e.classList.remove("loading"),e.classList.add("ready")}function ve(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:document.documentElement.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{fe(e,t)}),fe(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");i.addEventListener("mouseup",()=>{re(t)}),r.addEventListener("focus",()=>t.classList.add("has-focus")),We(t,i,r,e)}function We(t,e,n,r){n.addEventListener("input",ue(()=>{Ue(t,e,n,r)},200)),n.addEventListener("keydown",i=>{i.key=="Enter"?Je(e,t):i.key=="ArrowUp"?(me(e,n,-1),i.preventDefault()):i.key==="ArrowDown"&&(me(e,n,1),i.preventDefault())}),document.body.addEventListener("keypress",i=>{i.altKey||i.ctrlKey||i.metaKey||!n.matches(":focus")&&i.key==="/"&&(i.preventDefault(),n.focus())}),document.body.addEventListener("keyup",i=>{t.classList.contains("has-focus")&&(i.key==="Escape"||!e.matches(":focus-within")&&!n.matches(":focus"))&&(n.blur(),re(t))})}function re(t){t.classList.remove("has-focus")}function Ue(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=ye(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` + ${ye(l.parent,i)}.${d}`);let m=document.createElement("li");m.classList.value=l.classes??"";let p=document.createElement("a");p.href=r.base+l.url,p.innerHTML=c+d,m.append(p),p.addEventListener("focus",()=>{e.querySelector(".current")?.classList.remove("current"),m.classList.add("current")}),e.appendChild(m)}}function me(t,e,n){let r=t.querySelector(".current");if(!r)r=t.querySelector(n==1?"li:first-child":"li:last-child"),r&&r.classList.add("current");else{let i=r;if(n===1)do i=i.nextElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);else do i=i.previousElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);i?(r.classList.remove("current"),i.classList.add("current")):n===-1&&(r.classList.remove("current"),e.focus())}}function Je(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),re(e)}}function ye(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(ne(t.substring(s,o)),`${ne(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(ne(t.substring(s))),i.join("")}var Ge={"&":"&","<":"<",">":">","'":"'",'"':"""};function ne(t){return t.replace(/[&<>"'"]/g,e=>Ge[e])}var I=class{el;app;constructor(e){this.el=e.el,this.app=e.app}};var H="mousedown",Ee="mousemove",B="mouseup",X={x:0,y:0},xe=!1,ie=!1,Xe=!1,D=!1,be=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(be?"is-mobile":"not-mobile");be&&"ontouchstart"in document.documentElement&&(Xe=!0,H="touchstart",Ee="touchmove",B="touchend");document.addEventListener(H,t=>{ie=!0,D=!1;let e=H=="touchstart"?t.targetTouches[0]:t;X.y=e.pageY||0,X.x=e.pageX||0});document.addEventListener(Ee,t=>{if(ie&&!D){let e=H=="touchstart"?t.targetTouches[0]:t,n=X.x-(e.pageX||0),r=X.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(B,()=>{ie=!1});document.addEventListener("click",t=>{xe&&(t.preventDefault(),t.stopImmediatePropagation(),xe=!1)});var Y=class extends I{active;className;constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener(B,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(H,n=>this.onDocumentPointerDown(n)),document.addEventListener(B,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){D||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!D&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var se;try{se=localStorage}catch{se={getItem(){return null},setItem(){}}}var C=se;var Le=document.head.appendChild(document.createElement("style"));Le.dataset.for="filters";var Z=class extends I{key;value;constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),Le.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } +`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=C.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){C.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),this.app.updateIndexVisibility()}};var oe=new Map,ae=class{open;accordions=[];key;constructor(e,n){this.key=e,this.open=n}add(e){this.accordions.push(e),e.open=this.open,e.addEventListener("toggle",()=>{this.toggle(e.open)})}toggle(e){for(let n of this.accordions)n.open=e;C.setItem(this.key,e.toString())}},K=class extends I{constructor(e){super(e);let n=this.el.querySelector("summary"),r=n.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)});let i=`tsd-accordion-${n.dataset.key??n.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`,s;if(oe.has(i))s=oe.get(i);else{let o=C.getItem(i),a=o?o==="true":this.el.open;s=new ae(i,a),oe.set(i,s)}s.add(this.el)}};function Se(t){let e=C.getItem("tsd-theme")||"os";t.value=e,we(e),t.addEventListener("change",()=>{C.setItem("tsd-theme",t.value),we(t.value)})}function we(t){document.documentElement.dataset.theme=t}var ee;function Ce(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Te),Te())}async function Te(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let e=await A(window.navigationData);ee=document.documentElement.dataset.base,ee.endsWith("/")||(ee+="/"),t.innerHTML="";for(let n of e)Ie(n,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Ie(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',ke(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let c of t.children)Ie(c,l,i)}else ke(t,r,t.class)}function ke(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));if(r.href=ee+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&r.classList.add("current"),t.kind){let i=window.translations[`kind_${t.kind}`].replaceAll('"',""");r.innerHTML=``}r.appendChild(document.createElement("span")).textContent=t.text}else{let r=e.appendChild(document.createElement("span")),i=window.translations.folder.replaceAll('"',""");r.innerHTML=``,r.appendChild(document.createElement("span")).textContent=t.text}}var te=document.documentElement.dataset.base;te.endsWith("/")||(te+="/");function Pe(){document.querySelector(".tsd-full-hierarchy")?Ye():document.querySelector(".tsd-hierarchy")&&Ze()}function Ye(){document.addEventListener("click",r=>{let i=r.target;for(;i.parentElement&&i.parentElement.tagName!="LI";)i=i.parentElement;i.dataset.dropdown&&(i.dataset.dropdown=String(i.dataset.dropdown!=="true"))});let t=new Map,e=new Set;for(let r of document.querySelectorAll(".tsd-full-hierarchy [data-refl]")){let i=r.querySelector("ul");t.has(r.dataset.refl)?e.add(r.dataset.refl):i&&t.set(r.dataset.refl,i)}for(let r of e)n(r);function n(r){let i=t.get(r).cloneNode(!0);i.querySelectorAll("[id]").forEach(s=>{s.removeAttribute("id")}),i.querySelectorAll("[data-dropdown]").forEach(s=>{s.dataset.dropdown="false"});for(let s of document.querySelectorAll(`[data-refl="${r}"]`)){let o=tt(),a=s.querySelector("ul");s.insertBefore(o,a),o.dataset.dropdown=String(!!a),a||s.appendChild(i.cloneNode(!0))}}}function Ze(){let t=document.getElementById("tsd-hierarchy-script");t&&(t.addEventListener("load",Qe),Qe())}async function Qe(){let t=document.querySelector(".tsd-panel.tsd-hierarchy:has(h4 a)");if(!t||!window.hierarchyData)return;let e=+t.dataset.refl,n=await A(window.hierarchyData),r=t.querySelector("ul"),i=document.createElement("ul");if(i.classList.add("tsd-hierarchy"),Ke(i,n,e),r.querySelectorAll("li").length==i.querySelectorAll("li").length)return;let s=document.createElement("span");s.classList.add("tsd-hierarchy-toggle"),s.textContent=window.translations.hierarchy_expand,t.querySelector("h4 a")?.insertAdjacentElement("afterend",s),s.insertAdjacentText("beforebegin",", "),s.addEventListener("click",()=>{s.textContent===window.translations.hierarchy_expand?(r.insertAdjacentElement("afterend",i),r.remove(),s.textContent=window.translations.hierarchy_collapse):(i.insertAdjacentElement("afterend",r),i.remove(),s.textContent=window.translations.hierarchy_expand)})}function Ke(t,e,n){let r=e.roots.filter(i=>et(e,i,n));for(let i of r)t.appendChild(_e(e,i,n))}function _e(t,e,n,r=new Set){if(r.has(e))return;r.add(e);let i=t.reflections[e],s=document.createElement("li");if(s.classList.add("tsd-hierarchy-item"),e===n){let o=s.appendChild(document.createElement("span"));o.textContent=i.name,o.classList.add("tsd-hierarchy-target")}else{for(let a of i.uniqueNameParents||[]){let l=t.reflections[a],c=s.appendChild(document.createElement("a"));c.textContent=l.name,c.href=te+l.url,c.className=l.class+" tsd-signature-type",s.append(document.createTextNode("."))}let o=s.appendChild(document.createElement("a"));o.textContent=t.reflections[e].name,o.href=te+i.url,o.className=i.class+" tsd-signature-type"}if(i.children){let o=s.appendChild(document.createElement("ul"));o.classList.add("tsd-hierarchy");for(let a of i.children){let l=_e(t,a,n,r);l&&o.appendChild(l)}}return r.delete(e),s}function et(t,e,n){if(e===n)return!0;let r=new Set,i=[t.reflections[e]];for(;i.length;){let s=i.pop();if(!r.has(s)){r.add(s);for(let o of s.children||[]){if(o===n)return!0;i.push(t.reflections[o])}}}return!1}function tt(){let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t.setAttribute("width","20"),t.setAttribute("height","20"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.innerHTML='',t}G(Y,"a[data-toggle]");G(K,".tsd-accordion");G(Z,".tsd-filter-item input[type=checkbox]");var Oe=document.getElementById("tsd-theme");Oe&&Se(Oe);var nt=new J;Object.defineProperty(window,"app",{value:nt});ve();Ce();Pe();})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/navigation.js b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/navigation.js new file mode 100644 index 000000000..1628edce9 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/navigation.js @@ -0,0 +1 @@ +window.navigationData = "eJytW21z2yYc/y56nW5rmnZbbre7OE/Nnb14dlrfrtfrYQk7LBh5CCXxdv3uO4RkgYT+IKRXac3vSYAAgfTlv0jgVxGdR78RJjBniP4enUR7JB6j82iXJjnF2Y/fqrJvPzyKHY1OoifCkuj89CSKHwlNOGbR+Zej1IqwJH2pZWKKssyUURBT7e3pL99PjiIXSXL9jJmYkkxghvn9XpCUZbVqIbZBsSncQWvkfv/hRKWKziORJW9I9ga/KolIz8A5OkzyzQbzzwS/OL1NeJjnhKZrl5HEhKvPebrHXBwmaOtjpMHDPK85T32bT8cGusnWd9pI0AD9O0b8PCRwgE/Vi728KvAIfvfrv3Hsd4UmZQxvz54y2l1eKD0gvsV+V6ygYV53AnO0pthlVOHCXGY4IWiZ5jx2GmnQwV5F7czQvodnRQnznqcZkQ3vMqxwLRddi6c7kuGbnG4IpThZ4Cynzg5hZwVejdKakidnq2nQQV4LLG/anhdrkgb5r4h4XOAspc+YO+94GyfM/c8c54Rtl4IjgbcHl3EDPornkvzrbGULJcx7gVEiR5PJQeCl4BjtLlMmeEqpe2qBuMPSKDVff4Uew3Hy1/1E/uJ/7U3eeCn+yXHmvO86iWPkuMIblFPRv0N00EfMFNJIBnWMLLdyfUHikCwGdZws5YV5rpFg9hiJ5P+uUob9JhCIO1aaz4jmwXE08rA8K06E/DtHxLvT6Jwwd59JxTqL6BrFukw9xTq1NGxgYk3Bd/lo4wS6Fw0/J3vseUO1CGG+D2SHF4htsdOwRoY5fWIJ5vRA2LaYv70eCWycoe7lsNw3gEEbmmFJ2JO/tUSP4XixTrm4RJSuUdzT3qCOkeWSphkOy2JQx8iyFCi0XgzqGFnkyBtYLwZ1cJaet8dI90WhcolYjGlAJVjY4ySa5zQ4j84dJ01of22TA/Mspk7XxTRMu1p5+D2RmegxHHs/gzjoI2Yq7u+wPIoaePKw32OWzNJEGw3EYd84bziCGiY//frz2/enfY84zI2ntpuJHGD5gojACWSlEOEWxbkF4gLwqCADTIq6aI7ZbSMNFm6mBlhaPB+ofbDyF71ztrwBljUKaHj9iuNcpP38KlJ/O7XN2NOuIoXYqS3FnnaK5LK7Zglh24fDHuooNSi8m1yz5H6jRqHiKA22M6ADTI2jGA6eJLVjOMnhweT9TRAF3EvEAAvVEZZYCMuhQdvPAg83NzcwbNNnK0AXZawQzX0yRwDr3tggc2cTdFGGhUgZPThMJWSYyWEpkICGkBo0xChOOTQ7K8CwybmWf0acyOZoTc5v3poW73oMRg75AjNEv3la3O2ikIFe1nNcq5eGDPTyOyOymkPUgWmaTySgvwKP4mg7I/LwrmkjpmicEXnHKHij5ACezzzStNhjZgpoJIMZmMW+S24116GBbrb9YqtXDQx0MnYZrBafFtNA7a5dBquNCR7FsW8vdrDHzNTcZfDPo5i9snw19xkoiZH5Ek312qdWaMqf2d/7/Ej07YpNzuLijMTQkRhT7MOZ8ZaE/mKdXUJiAInlo/7uqV1CYloSer1MePqS6S1S1UlZ4FUf93vMjBuqDlPJlBAwi9w3bAeRv3qlWORMkB1uPA5W7+QWMjoEeidXglsnZWp9WOho5a4n4snh7spWMYWOLARaeHL4A+2sPa1ky2KAb1Zog92u12ZzULJfp0hfIh/bpCryapglFg/yX7YkR6ESBFyNW8MqoF/SFUE03WbtCyoLvC5nkgvR8SpeJaMg0OnzDaH4htCuLddKqIZBYjOcZWiLFQk64a1kbQTIQN69Moq/g5UBHsejZ9zPwsqALBojQ92DKkXLuGD0wDu2SSG+LAfoVZ1AEhUGnDpwZs5jbZkKA00fZfVBMhUGkFkhzgjbQiolBLwzi2dIy42pfve6L1eI0KzxVFyN/qVMDXGN/ubhlxr6S5GO463WyC83OK3r2FKnKG8uZ/Qq2dmXCCVdFkOdbbMByPebDcy9sM8cNf3CMntoCtbeWbGhbnnPYmuXPHKbhwsN9iynguzt3fqoUYHALnlDkW2qKH726pC3WEiwLYkSKQFgimXMMWaWHGWB39Ko6yOHSmRh+6ShlQKUUH9Bka63to4Sjle2brHo6JeVgkIAHeQWi8ucc2OQsMqUKFhqzskO8YNDqkTBzXzIBN5ZWrn43auRr9kz4SnbFV/j6BOVXtNKrgEFp/6lS0whII1LtEdrQokg+oCo1ZUS0mFAvWvhATENBWh9RCyheE6R2KR8V6wmeLoHZO0EwIGw5/TJOhqVigoArTqyi9nVhzNAokTAGosZrLCYufiuDBIBalwh/mQe9VtkKhCshNe5dWCtZSQC1JgSlr+CGgUC1JihGFSYoRjkq89DoZviiAGHj9Vs2h47VrOp58BhfqlVZ5AKqhS4igWmKUq66KrU8dwvj5s7n9uPhfCarXlk3SWlYzwU5eGlPJwlKQMTtoD90nZ+DGWHwqNtk+H++sjBgeyaH0HX61zbt8/vTr9//R/5AFZf" \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/search.js b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/search.js new file mode 100644 index 000000000..69ddd6bcd --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/search.js @@ -0,0 +1 @@ +window.searchData = "eJy1XW2P2ziS/i/O176si3oPDgdkkpndAJObbDKzubtgMFBsdrc2askryelkg/nvB1KSXSSLUslyPiVok1VF8mGxWA8pfts09WO7efbh2+ZTUe03z8KbTZU/yM2zzfPDoSx2eVfU1eZmc2zKzbPNQ70/lrL9C/rt6X33UG5uNrsyb1vZbp5tNn/ejNLis7i/FXt5knN7rHaqsilJFSHE3WwOeSOrzrKJ1PLuvn6c0aKKrNTy92PRzWhRRS7RclbygxoZ2Th9P/yd2++/HGT129ufCXNHQUOJaWtHayhLX+Rl6Zip/si10RBwNlCL8MhBpunapNwfvv63+o9Hcv/r5bJfvfRLfvVykVyxzRKIhNEhvxy0xJOO7utBoj4Zfl6kBkR6UvH2WHXFg/yxaeozxgYpvRZcYpGaCM4t2dVV2zXHXcfW8sSs4tdoVKWBWRaHj3Xe7F10jr9wIfpOdr/KL9SsP4saysz01ckmUs+skss0nBW8LPKyvmudHhn+zu2PV9VtTdg5SlE/T1s52kFKf583VVHdTSgYSqzQYSLf1cDA/aT8vx9layyeroqxyAotynn/VJSUkxu1jEVWaHmXf5YzWsYiS7WIKHYa05eyHWBRdbK5zXfSbZpRYakFsBV4Kape3Nd1K18WjVQuqJAXWvBkl1c7LWpviJo0i5Y3a6yqsdrM20HIlQ1sZN5dqTe1qO/Wmyow/Fux3/dVLrWzva8f77WU79Cfb2Vbl59l+7ws8vZiE5tBSn6SckUTn5dl/di+PpZdcSjlO1nKneEFl5maa2kPg7QWSbuiyWrf8eOXTlbt5YbeF3spkYzrTqK+G3twrrV0l1d9P/Yo/U5G/6rmaquKv8l3n/I72T5v1zuBTktVE+swSM3b7+YPeiT/0t3LRtXR8fcqGNdK1C0SdUVjfyrKTjaX2nd7qn1NCBQdihgWDvRQ94rmvJZtm99datDDqfYVTfrh2HV1ZYT8y6z6qAV0vYArGjbO1K8X2rVH9a9pluzy3b3cX2rVufo6o3DMOsa97JiVrLAqZlXSKpzoWKRVTf6x+nS30E29UiA9YeBFgfRlxs5GfhwzeYHfYgOXBNIzZi4OpBcZuyCQnrBzaSC9yMQFgfSEiUsD6UUmsqPSCQOXRaVLEbk4Kp1G5WVR6SKjV0SlE7avjUoXNWFpVDph90VR6SJjGVHp9MLEikqXQWAuKp0aaFZUusgcRlQ6YRA3Kl1kEi8qnbBqQVS6yDBWVDphFz8qXWYWIyqdsooblc4ZhaPSAVbsoJQqvyomnZ1mXo3MWUa28PJJ5jeHO8eWGNRPsctGZJhe825xiUEcDPstYkN4xiSM4L6LpuzpS6xC6c/5R1kydTwph8LTTRzs9ih81b7Iqx1fZ9HuxvLr1L6Ut/mxnPTopt79qcJCxXgQlbvqY4ApxedSqwbzZdEeytyk/Oe1Pdn31Vg7YNQejxFv8q6TzSRubQMOpyoXKEd85mdZdS6v2/+ZfVqkGjP3BNU3iDqXmR6uwSCPnin5a+TuJi3frbH59nZC9O3tKsnPyRMwZ+HP507BTMn/8YE8rzRIV78ulB2cD3j8amxAPudNkX9EuNM/L5ROnYb5mO8+WUdhBg3jrwuV4LMw7/OibHUJ54zKoORcYqEa7lkYWg/rLIxT1eebjBzhjOJJd8jWuM+7nKtxKLtSYyurvWR376n0cq1ok1vmxHka/Veu2/2r7FR5Yo72Yobfp7HX20FZ+G7XSFm5Ng5/X2Al7aZGOX2BaStHW3wa3jTFQ462eKSWodA6TS+OTYOnPKlpKLRUk8EQFP8mA5JRi/p9qXwD8++LfXfP0vDkcSg6qUeX96YlZXF3T8aRrrb7sewidbjv3srdpC71+6q++x+W9CdfZhuhLfUo+V+eEn9egqNkFgVnRTwUTCmbR8FZGxMFljpjBukik5jT/65CAjrOO63gSeHf6JrFfap8WxRK2eRCzFP3bpeX8qfciDtmtLaqym0+GXfwlE9OMKxyfopNK5qcZFjR/DSb6c45f477sS+7Rt0P9bHak6kiSuHHsfQalW/uv7bFLi+XqT4Mta5iwvu6+fS8kTlX+WPdfMr78tdo+VL1Y9uvZMar1g6D5lxSezhVWKP4bd2ZN35m9Dbn8gvVovn0te3kgxuc6j9zY9Oi+lx/orb+g5j+95kFojeEPu/evsybT69r8hLToONc5nI9L/JD/rEoiw7TkY4mXOpyXT9Wn4umrh48QXCvChVa03vvi2qvbppNdN5QZI2Wn4vq+GVKhy6wRsPrfDcl/3W+WyP9+euXcTglXxdYpeHt60n5b1+vlD5jvyqwahbKj0dqn3yegqrA5Rr+llf7Ur4p8+62bh403dbUB79CuvxC/cYdiHfG1Rrsf3uNfYGFGsyIosmrPb5eM6nkycdz8SltQ52F4bWrbiq65ijyBteuqunYmqPsH7LxHXoh9H0+lV6mEuMD+eMZoFgl1yDmebOjt5Z+TU/yvs5kS+3GeNlJPOmZ+vdDpWsY8Mu7pdrriQB4oeoLhvlJ3RZ9rWuYMHq3Sww5DHXXmYP4hj5KIAiH/ofJeBFtL17/7ASc719Td6A9kZPSSawKSkb/4/R0U/pJyW9lWed7j+T+xyWSMcvRM8++C7+n3yb7gH+115THvNR7NnCqCaXcv5X/7G8CTTXHKXfFptGyFzTTbYRn9h2a+qFoXeJm0o5zpUttwGvOUFrB+k0v+OfiE7nI0iUn+11sw/MAd/d0nm9C7pOhzmRDbdM96nfewxlTBswc0Zg3YbKr3xfd/XBcmT5JOVNlsvMnkbZQBxd03rZ5zBpOWV9s1rn+lc36p4ecYFo1VL+qUXW1G2f2xZaZMlab57pvVMvn1VARvvPAXxGZkfekL8tt3ITGd7LrcG8zFLenKqv1V1/5iit/cpCvscl37EF7MhReq9PyynNa++L/Ia6gWX1epD66p0J8qs/l12puSykPbL1j6dWja/q02fFd6sP8ek0XP694qUv3aX4kV9c5/apWg2pdYMXCcNNFOT/S/F5T7Gp60WlAnuaJHMqCyY1jzdmZvSzI9Le3Q5mU+cZ2EzkUvtbbosrLkr9UnMtfovmk9j915FHl5X85e+0/xp/+mA5P8SE9c+M/tgFJ8mYAkOnIJk9n/VW6DpBW8uRO+p3fWdFQxafvTd0WBuU2o/RwLr9S8wtZdcRZOY/e3Vh6rdayJuI+n9Kh8EqdL4tWgVUx9y+038wLfFZ6xoh9X1tx+Tuj9kqr+kTRhUbJ6rvY9FO9O7JtuB0Kr9bZ7KSV+JrV3OxkM1ZZq/9Ylq15xGhOPa6xUvtfZfdukfI72V1R9//V9cMCzf/ui6/Uax0fm1E7c4CMrxV/gnROZ192pcZXrZ5PxPbQo7Zob08V1utejOuivSayFR3+pVDhwYL2P6Aq6/UX1WL9qMpK/WPrucofzuXXah4awdZ8Lr9Ss8G+zmidpF/ZGtXHWl7Kz7/WdcleuOqDOvj/uRvqrLTgrSzzrvgsl0ZyzVDvehHdskX0WuvnWxWGGITYrOJzhfW6u7pZonkovlLvO9ktHe5Wdtcb6XfqqsVj/rX9pfq1dhNFfhNyXauuutqfMFpixQ/57tNdo458vqjL+sjeTrSy+3iquhurrrfnpyZ/kKVs2b6gld0tqnMFC05LaH83tg/z2bNS2XOS0N/1licJ6617nX8xDjDPW/OQf5k8xrxIe1Et1V5UV9N+qaNuZXd9X63tWeg4tSFX853vZLdwMK42EubnIeYVT38TYonmJfud9lr7HeNZgjmlfdm1GpcM7FVG9df67q6Uy/ccna53zX1Hb8nS6Lu343ox+NAfS5ejoTuutyL9Vi0fk2N1zfH4rVo6FsfqeuPwW7V0N3SsrrcfMm/lzSievpfH1rnExV3HvymNr9joUjqLK+BKaf2FYGcn1E6Rs4v0vpUtnydQmpuhwtoc1vT59Lmslq49nslUH33bNxfvA+yPNNic0EirnozrP9uATPPXuYjEmTfon9LgWFkG9XW+i0HjKbxFFp0qfReTfvwid8eFfTTWucQgdPTNicrRwSWkfyx2gTbjqNSXJXombqeiSXJqgUcleZ3Pq9LPv06r5Hx7ACmd//zAfF8++u6eU3r4y9zUdwjuvTfQSZ18DqFX+vvNpqj28svm2bfNeG3j2UY8DZ5mm5vNbSHLvXo2bTPmMOuH4Rrdvt4d9X9/H4r9Q/sPVbgv/Zft5ubD9ibaPg0h+P33mw9jZf2D/sMo4/wXXRE2Nx/gJoyeBtvQqAhORTAqis3NB0FVFE5FYVQMNjcfAsrUwKkYGBXDzc2HkKoYOhVDo2K0ufkQURUjp2JkVIw3Nx9iqo2xUzE2Kia+iolTMTEqppubDwllaupUTI2K2ebmQ0pVzJyKmQkAhYeMqgkudsACj0YPiTsg8GMCCBQsAG6C5GkozLouhMDEEChkgCAVuzACE0eg0AEkBMGFEphYAoUQIGEILpzAxBMolAAJRXAhBSamQCEFYrKyCyswcQUKLUAiC1xogYktUIgBEl3gwgtMfAkFGSABJlyACRNgQkFGkAATLsCE5aG0iwKyMuGkTIQJhRlBIky4CBMmwoTCjAhI7+giTJgIEwozIiQruwgTJsKEwoyIyMouwoSJMKEwI0i/JVyECRNhQmFGJGRlF2HCRJhQmBEkwoSLMGEiLFCYERmlOXARFpgICxRmgi1Z2UVYYCIsUJgJyOUzcBEWWOugXgjJJTQglkITYYHCTEAiLHARFpgICxRmgvAmhKeZVdcFWGACLFCQCSKqrouvwMRXoBATkPgKXHwFJr4ChZiAxFfg4isw8RUoxAQpZbULr8CEV6gAE5AOLHThFZrwChVgQtKBhS68QhNeofC6kdCFV2jCKwy8biR04RVaoVbodSMhEW2Z8AojrxsJXXyFJr7C2OtGQhdgoQmwMPF6gtAFWGgCLEy9niB0ARaaAAszrycIXYSFJsKirdcTRC7CIhNhEXg9QeQiLDIRFgmfJ4hcgEUmwKLA5wkiF1+Ria8o9HqCyMVXZIXzkdcTREREb+Irin2eIHLhFZnwihRgQjKgiFx4RSa8otTb1S66IhNdUebtahdckQmuWMElJAOZ2AVXbIIrBl9vxS62YhNbsYJLSIbZsQuu2ARXrPASkmF27KIrNtEV650iGWbHLrpiE12xwktIhtmxi67Y2jAqwIRkmB0Te0YTXrGGFxkExS68YhNesUJMSK5SsYuv2MRXrCATkatU7AIsNgGWKMhE5KxIXIAlJsAShZmIRGfiIiwxEZYozEQkwhIXYYmJsERhJiIRlrgIS0yEJQozEYmwxEVYYiIs0fkIEmGJi7DERFiiMBORCEtchCVWWkJhJiIRlhCZCRNhicJMRCIscRGWmAhLMt9mP3EBlpgASxVk4u1NkD0NRWJUTl2ApSbAUgWZmERn6gIsNQGWKsjEJDpTF2CpCbBUQSYm0Zm6AEtNgKUKMjGJztQFWGoCLFWQiUl0pi7AUhNgqU56kehMXYClJsBSnfiiE1guwFIr96UgE5PoTIn0lwmwVGEmzkiQuAhLTYRlCjMJibDMRVhmIixTmEmArOwiLDMRlinMJCTCMhdhmYmwTGEmCYjVOXMBlpkAyxRkEiocyVx8ZSa+ssjfXS6+MhNfWezvLhdfmYmvTCEmIZGdufjKTHxlOrlKBp2Zi6/Myq9mXieUESlWO8eqAUan37ZUmtXKs27BN9L9T3Z1K9O6Fb7B7n+yq1vJ1m3gnVv9b3Z9K9+61Vij84dbIuO6tVKuW403T46aSLpurazrVoEopdPUWyLvurUSr1uFo5RcQ/rf7PpW7nWroJTS2eotkX3dWunXrU7u0wnrLZGA3Vro01n7lM5ZU0l+J8uv4JTSaWsyz2/BTyfvUzpzTeX67WS/zt+n9Oyh0v12vl+n8FMaf1TG30756yx+SuOPSvrbWX+dyM88NAmBPzvxr3P5GY0/KvVv5/51Oj+j8Udl/+30v87oZzT+KALAYgBAJ/UzGn8EBwAWCQA6r5/R+CNoALB4ANCp/Yx0/EAwAWBRAaCz+xmZcACCDACLDQCd4M9o/BF8AFiEAOgcP71wAUEJgMUJgE7z02sXEKwAWLQA6Ex/RuOfIAbAYgZAJ/thS08AghwAix0AnfCHLT0DCIIALIYAdNIfth7CkICgxRJA0DOd9BwgiAKwmALQyX8PhgiuACyyAHT+H7b0JCL4ArAIA9AcAGzJnC4QnAFYpAFoIgC2tBsniAOwmAPQbABsaT9OsAdg0QegGQHYpnQTCCBaFAIEPRBpJBMsAlg0AgTebSwQRAJYTAJocgA8hDlBJoDFJoAmCGj9BJ0AFp8AYU+50+c9CEoBLE4BNE0AHuKdoBXA4hVAUwUAZLoaCGoBLG4BNF0AQMayBLsAFr0AmjEADwdPMAxgUQygWQNvHxIgtGgG0MwBAO3OCaYBLKoBwh6EtC8h2Aaw6AaIehjSCxLBOIBFOUDUw5CeRwTrABbtAJpKAJrYB4J6AIt7gKg/AEKvCAT/ABYBAZpTAJrgB4KDAIuEAM0rgKBXBIKHAIuIgCj2I5ngIsAiI0DzCyA8R1EIIFqEBGiSAQQ9FQhSAixWAqIpIBLMBFjUBMTbialAsBNg0ROgKQcQ9JpEUBRgcRQQ+w8hESQFWCwFxIF/DAmeAiyiAuIehfSSSHAVYJEVEEcT/pTgK8AiLEBzEEAfvQCCswCLtIC4hyHtCwjeAiziAjQXAQHtCwjuAizyAuJsIrIh+AuwCAzQnAQEtDMhOAywSAzQvATQDC4QPAZYRAZobgIC2pkQXAZYZAZofgIC2hkQfAZYhAYk/aE42hkQnAZYpAZongICz/k0AokWsQGaq4CAngsEtwEWuQGar4CAhjLBb4BFcEDSI5GGMsFxgEVyQM9y0Mc9gCA6wGI6QJMXQFPMQJAdYLEdkPqT0UDwHWARHqA5DDq2IRgPsCgP0CwG0GwzEKwHWLQHaCYDaNYYCOYDLOoD0v58Jj0RCPYDLPoDNKMBNHsMBAMCFgUCmtUAmkEGggUBiwYBzWwAzSIDwYSARYVA6s9VA0GGgMWGQNaj0HPgk0ChxYiAJjmAZqOBIEXAYkVAEx2+nR5BjIDFjIBmO4CmtIFgR8CiR0BTHkDT2kBQJGBxJKBpD6CpbSBoErB4Esj6w8L0VCCoErC4EtD0B9AUNxB0CVh8CWgKBGiaGwjKBCzOBDQNAjTVDQRtAhZvIjQPAjTdLQjiRFjEidBMCNCUtyCoE2FRJ0JzIUDT3oIgT4RFnghNhkBMn2Em2BNhsSdCsyFAU9iCoE+ERZ8ITYcATWMLgj8RFn8iNB8CNJUtCAJFWASK0IQIfVmE4E+ExZ8IzYfQ/JMg+BNh8SdC8yFAs+mCIFCERaAITYgAzagLgkERFoMi+osSNKsuCApFWBSK6C9L0My6IDgUYXEoor8xQbPrgiBRhEWiiP7WRExPBIJFERaLIsDPGguCRREWiyL6yxOJ5zIAgUKLRhH9BYqEnkgEjyIsHkUMlyjoiUQQKcIiUkR/kSKhJxLBpAj7LkXPpNAn9KnbFPZ1Cs2MQELPBOpGhXOlQuOQZuAFeavCwqHmRiChZwJ1s8K+WqHJEaC5dEHdrrCvV4jIv1kT1A0L+4qFpkdIMl5QdyzsSxaaHSHJeEHdsrCvWYgJZ0hdtLBvWmhqxDMNqbsWFpUiNDNC7zAEwaSMf9N3Hz/LppP7V/0dyA8f+o/rf9v8MdyKVAlBrUVdkFQpv2ff/vzzfA/y2bc/0VVI9ZvSlJdl/dg+HMuuOJSylWX/9AiWe/7g77eNyBZIrbt72agb7frCMhZ5fjzo2yYIeiHRliv69LF+3HbAbQ94otTn97EMgWWEPBmHQ1nscrvP0Egwm6Wf5kLGbHGDmN0+PvN6FpOgsVMHQ1hSTo/LneVkWI7gyqkfW3WR/iwmPEthytAf2zJEBGcZMQ/jvRCjX0I01DFvhHopnfzSGUiOEZKTAcnMtn0tDAyjLmYO91ddHg/3WUTKErGzfAhqTdxXSdhy1LfjsKwINSjhi6kP+nUtA30YxExB/dfw0YQCBD7Fa+mKihbo/xPzfEYvWPZPXOHpim0MuJ1/+mzD6Z0ebDAWGcGFInfnD1og2QkaaBUjXCZbnr5MgUUnWDRvKriiS/2aFe4N7JzDSy1uTt8bwRZH2GKeT6FEj99WwaJDLPrSMbTetsC9EuFeiZfIl/tmfGjMhTPgUCJcMudMb63ImbOgmI+G+7pu5b5o9GgVZvQg0BLQA+xmE/KHTYtWYYkpFI/U4B0WwKyReee1F+Nr8Dchuyv6mOy+2O9lJb90smqtWCNAYxXAIJ7rgfDT1mgpwqMG3F7orBAGB46K+GJJGZ40QGICNNyKuWGJKYvDxzpvrCAR+2imnNryygFeRhKmFPwwDJ5lOKgalttsWI+C8T8hjCsUr+n7vMuNJR1pSXkeaHinFS9vRtfx/MFedvnu3ozTAwSJsYXjEhwzjSvysr4z3SD2gjycjVPV2AEECPb9Gn6ziSKmPPpBCowcbGXCm//7oj2UuRPnhagbY15oJR8KI3CNUASQ8NoohydWMS6wowh4Xe95JgP3VIx7imvc6aVaw5NhHwS8Tkei+jdycYPxwih4XtZdXrFRvFhffpZWL0XIkpgnRC16t0VpOdgQjSHTSytBDiaRPSFvKve2mJkB5OyDYW1nxr6nJ5Kwx8cxNHOm3Ja56V5iZFPK8/jDMywY03imJMxuxk+qYFkYP8ytFf56KBaFOyjh4Vm/sYRl4HUs4S0Od+qz5+bWE036lNc/d7LbHZvGmvIx6p2U3SI16oYUtM6nPL9xJ7tDUzzk5qIS4005b6zQczKom/EEg4Q340/Pw2A5eLhS3nD5vlKJ9ujYZ4shmlEZdJb44VNwKJOBjMyGCCgb9+rp8AfBjET6l2NQFyBTR5G84EpJIoNw7Gh6q242IW+0zSwQ3plnQwwCgrcGOquVsY4yRfQf3TMnOEZezJtSRfW5NrfwKY5/gOfTizZ/2KtTewhoOBQFpjFt3hiTIMN7CuYSrIVYtuDNABPrResmp0LUNcxNctHu8+bTQ20iO8UjDjxMF60T72d4qJhpCyXmNj+WxjQOcUKRCeLze0vYaeFRZy7C5tNJWBgefaZzL9qyqI5fjF7C04K571CvKe0MIdgZcScpfpIJtwvDMeV2N3peCcvCEGCux0VLrH8pHrktd74+6o8bm0kJHKoAr3Vl/tGaaaiLmCnf83fPcffgicZc1h9k2+Z3hhy8JwyG5S0aCTGeWxqk9ttiInmOl/uIaejpW+u4xTiAZcZV9h4hRjMmHVbJbEx0iNPyzmt4fXtr7IfQyDLzMvXtrRWERmg8mFt0Mw6IENoTnuM0HnfH2SEc5TPZ0braGR0eIU/HTJ3V1cgGG4IQipghvvmiF0aSsf3lC1NhpxFzITQxF6lRjHe2YA6YyYsomeqLx7i30JTjyTB7aIsXqIDZtNYO/zI8Y8XgVdTVSI64Q96pSNDwJMgBM6PAw/3XttjlJcFMG6Qer5dGaY918ylvpJHmTPE6uuX5uXE/42Z58EIa8GBwfoYIR8548UvGbQsznCdJQdzMcNhmAXOn8a+jbB0T0TgwI9h/Hc1UIk7ZsAQ0uemlADM6wMy4NvqdewQo1NUZD+M9AWb2L54z4bg0MZdi90Uq7PUw4DPenCZyP1vsPMcTB8Bu8OmlKpw9x3FsxhzCno40+w7HROG4d2UerxgEtnlZ5K3Fn2FGeRDL5DpPjwvi5uIplPEW+abunDM+KQbtlomQY9UVD5I4P4BjW5aoNv8snRURNY1JZI9ivCsizhAymeB2l5fyNrf5LnwyQH2jhyfK3r1htj7j+b1eiJnTRVKYO6VWhTNGg2LkcpgJJPu5R4xLjKeMh2/yyUYsEy9k3N4yXl/EwvCWMOMFAFNPJ2LR2HVkvLmE30HEorC34KIMPWqIReGTj1vmtMRPimJZOBLIuIgjHjnEMo21mw1AehXAezN1i4IpzOk0zDcL5pFDdaLTOlgHeDbwpfSPEmJzcEDPjDNbImuOSSB1Q4MlR78biNwzEjGEgsxIVUnqj4C4B1fwUa4hHgiZ7bTGLkZ4SsfYZxuMpqZj+Mqc+qWUpofDB2HUdXiWlK9tJ42hSLFPY54HdNCFZfA6q7u3sogmQsMx3GFKKx6kfvkMd49hFA8YDuDx+ZRgOMcUDWhjdrnz3iGeBhhtzEjFfU4SC8TnrphRlP0uJBaHz1pxwaEOb7VqYh3y3af8TrZ56znKhanOYOjVkGm0fQgeL1RMQth8BBI3Gy96zLw/ftERi8KLHvOYFH6eEYvC6xPTiY8vO+E8Bt45Cd68eMyLstWHJowuR5KYZPdj3lTWYXhjj8iU0r+vhaJYtNpmYyZ0mK4Cxv8wz/33yXJzP4FdbXByTlx5vvOmeLEB5oGtxweTTdxiGQHPXZIZH7xnYq7IBn+TIAeejemx7SlJw5NocB34uG82dvr2RIbzoOuEHomR5+SZNT7oicXgwIzJNJ0e6MRycBjDxD96bhNLwjEVh4X9/WZzKA6yLCq5efbh9z///H+MQfbV"; \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/style.css b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/style.css new file mode 100644 index 000000000..2ab8b836e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/assets/style.css @@ -0,0 +1,1611 @@ +@layer typedoc { + :root { + /* Light */ + --light-color-background: #f2f4f8; + --light-color-background-secondary: #eff0f1; + --light-color-warning-text: #222; + --light-color-background-warning: #e6e600; + --light-color-accent: #c5c7c9; + --light-color-active-menu-item: var(--light-color-accent); + --light-color-text: #222; + --light-color-text-aside: #6e6e6e; + + --light-color-icon-background: var(--light-color-background); + --light-color-icon-text: var(--light-color-text); + + --light-color-comment-tag-text: var(--light-color-text); + --light-color-comment-tag: var(--light-color-background); + + --light-color-link: #1f70c2; + --light-color-focus-outline: #3584e4; + + --light-color-ts-keyword: #056bd6; + --light-color-ts-project: #b111c9; + --light-color-ts-module: var(--light-color-ts-project); + --light-color-ts-namespace: var(--light-color-ts-project); + --light-color-ts-enum: #7e6f15; + --light-color-ts-enum-member: var(--light-color-ts-enum); + --light-color-ts-variable: #4760ec; + --light-color-ts-function: #572be7; + --light-color-ts-class: #1f70c2; + --light-color-ts-interface: #108024; + --light-color-ts-constructor: var(--light-color-ts-class); + --light-color-ts-property: #9f5f30; + --light-color-ts-method: #be3989; + --light-color-ts-reference: #ff4d82; + --light-color-ts-call-signature: var(--light-color-ts-method); + --light-color-ts-index-signature: var(--light-color-ts-property); + --light-color-ts-constructor-signature: var( + --light-color-ts-constructor + ); + --light-color-ts-parameter: var(--light-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --light-color-ts-type-parameter: #a55c0e; + --light-color-ts-accessor: #c73c3c; + --light-color-ts-get-signature: var(--light-color-ts-accessor); + --light-color-ts-set-signature: var(--light-color-ts-accessor); + --light-color-ts-type-alias: #d51270; + /* reference not included as links will be colored with the kind that it points to */ + --light-color-document: #000000; + + --light-color-alert-note: #0969d9; + --light-color-alert-tip: #1a7f37; + --light-color-alert-important: #8250df; + --light-color-alert-warning: #9a6700; + --light-color-alert-caution: #cf222e; + + --light-external-icon: url("data:image/svg+xml;utf8,"); + --light-color-scheme: light; + + /* Dark */ + --dark-color-background: #2b2e33; + --dark-color-background-secondary: #1e2024; + --dark-color-background-warning: #bebe00; + --dark-color-warning-text: #222; + --dark-color-accent: #9096a2; + --dark-color-active-menu-item: #5d5d6a; + --dark-color-text: #f5f5f5; + --dark-color-text-aside: #dddddd; + + --dark-color-icon-background: var(--dark-color-background-secondary); + --dark-color-icon-text: var(--dark-color-text); + + --dark-color-comment-tag-text: var(--dark-color-text); + --dark-color-comment-tag: var(--dark-color-background); + + --dark-color-link: #00aff4; + --dark-color-focus-outline: #4c97f2; + + --dark-color-ts-keyword: #3399ff; + --dark-color-ts-project: #e358ff; + --dark-color-ts-module: var(--dark-color-ts-project); + --dark-color-ts-namespace: var(--dark-color-ts-project); + --dark-color-ts-enum: #f4d93e; + --dark-color-ts-enum-member: var(--dark-color-ts-enum); + --dark-color-ts-variable: #798dff; + --dark-color-ts-function: #a280ff; + --dark-color-ts-class: #8ac4ff; + --dark-color-ts-interface: #6cff87; + --dark-color-ts-constructor: var(--dark-color-ts-class); + --dark-color-ts-property: #ff984d; + --dark-color-ts-method: #ff4db8; + --dark-color-ts-reference: #ff4d82; + --dark-color-ts-call-signature: var(--dark-color-ts-method); + --dark-color-ts-index-signature: var(--dark-color-ts-property); + --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); + --dark-color-ts-parameter: var(--dark-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --dark-color-ts-type-parameter: #e07d13; + --dark-color-ts-accessor: #ff6060; + --dark-color-ts-get-signature: var(--dark-color-ts-accessor); + --dark-color-ts-set-signature: var(--dark-color-ts-accessor); + --dark-color-ts-type-alias: #ff6492; + /* reference not included as links will be colored with the kind that it points to */ + --dark-color-document: #ffffff; + + --dark-color-alert-note: #0969d9; + --dark-color-alert-tip: #1a7f37; + --dark-color-alert-important: #8250df; + --dark-color-alert-warning: #9a6700; + --dark-color-alert-caution: #cf222e; + + --dark-external-icon: url("data:image/svg+xml;utf8,"); + --dark-color-scheme: dark; + } + + @media (prefers-color-scheme: light) { + :root { + --color-background: var(--light-color-background); + --color-background-secondary: var( + --light-color-background-secondary + ); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + + --color-icon-background: var(--light-color-icon-background); + --color-icon-text: var(--light-color-icon-text); + + --color-comment-tag-text: var(--light-color-text); + --color-comment-tag: var(--light-color-background); + + --color-link: var(--light-color-link); + --color-focus-outline: var(--light-color-focus-outline); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-project: var(--light-color-ts-project); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-reference: var(--light-color-ts-reference); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + --color-document: var(--light-color-document); + + --color-alert-note: var(--light-color-alert-note); + --color-alert-tip: var(--light-color-alert-tip); + --color-alert-important: var(--light-color-alert-important); + --color-alert-warning: var(--light-color-alert-warning); + --color-alert-caution: var(--light-color-alert-caution); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } + } + + @media (prefers-color-scheme: dark) { + :root { + --color-background: var(--dark-color-background); + --color-background-secondary: var( + --dark-color-background-secondary + ); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + + --color-icon-background: var(--dark-color-icon-background); + --color-icon-text: var(--dark-color-icon-text); + + --color-comment-tag-text: var(--dark-color-text); + --color-comment-tag: var(--dark-color-background); + + --color-link: var(--dark-color-link); + --color-focus-outline: var(--dark-color-focus-outline); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-project: var(--dark-color-ts-project); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-reference: var(--dark-color-ts-reference); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + --color-document: var(--dark-color-document); + + --color-alert-note: var(--dark-color-alert-note); + --color-alert-tip: var(--dark-color-alert-tip); + --color-alert-important: var(--dark-color-alert-important); + --color-alert-warning: var(--dark-color-alert-warning); + --color-alert-caution: var(--dark-color-alert-caution); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } + } + + html { + color-scheme: var(--color-scheme); + } + + body { + margin: 0; + } + + :root[data-theme="light"] { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + --color-icon-text: var(--light-color-icon-text); + + --color-comment-tag-text: var(--light-color-text); + --color-comment-tag: var(--light-color-background); + + --color-link: var(--light-color-link); + --color-focus-outline: var(--light-color-focus-outline); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-project: var(--light-color-ts-project); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-reference: var(--light-color-ts-reference); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + --color-document: var(--light-color-document); + + --color-note: var(--light-color-note); + --color-tip: var(--light-color-tip); + --color-important: var(--light-color-important); + --color-warning: var(--light-color-warning); + --color-caution: var(--light-color-caution); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } + + :root[data-theme="dark"] { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + --color-icon-text: var(--dark-color-icon-text); + + --color-comment-tag-text: var(--dark-color-text); + --color-comment-tag: var(--dark-color-background); + + --color-link: var(--dark-color-link); + --color-focus-outline: var(--dark-color-focus-outline); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-project: var(--dark-color-ts-project); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-reference: var(--dark-color-ts-reference); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + --color-document: var(--dark-color-document); + + --color-note: var(--dark-color-note); + --color-tip: var(--dark-color-tip); + --color-important: var(--dark-color-important); + --color-warning: var(--dark-color-warning); + --color-caution: var(--dark-color-caution); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } + + *:focus-visible, + .tsd-accordion-summary:focus-visible svg { + outline: 2px solid var(--color-focus-outline); + } + + .always-visible, + .always-visible .tsd-signatures { + display: inherit !important; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.2; + } + + h1 { + font-size: 1.875rem; + margin: 0.67rem 0; + } + + h2 { + font-size: 1.5rem; + margin: 0.83rem 0; + } + + h3 { + font-size: 1.25rem; + margin: 1rem 0; + } + + h4 { + font-size: 1.05rem; + margin: 1.33rem 0; + } + + h5 { + font-size: 1rem; + margin: 1.5rem 0; + } + + h6 { + font-size: 0.875rem; + margin: 2.33rem 0; + } + + dl, + menu, + ol, + ul { + margin: 1em 0; + } + + dd { + margin: 0 0 0 34px; + } + + .container { + max-width: 1700px; + padding: 0 2rem; + } + + /* Footer */ + footer { + border-top: 1px solid var(--color-accent); + padding-top: 1rem; + padding-bottom: 1rem; + max-height: 3.5rem; + } + footer > p { + margin: 0 1em; + } + + .container-main { + margin: 0 auto; + /* toolbar, footer, margin */ + min-height: calc(100vh - 41px - 56px - 4rem); + } + + @keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + @keyframes fade-out { + from { + opacity: 1; + visibility: visible; + } + to { + opacity: 0; + } + } + @keyframes fade-in-delayed { + 0% { + opacity: 0; + } + 33% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + @keyframes fade-out-delayed { + 0% { + opacity: 1; + visibility: visible; + } + 66% { + opacity: 0; + } + 100% { + opacity: 0; + } + } + @keyframes pop-in-from-right { + from { + transform: translate(100%, 0); + } + to { + transform: translate(0, 0); + } + } + @keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; + } + to { + transform: translate(100%, 0); + } + } + body { + background: var(--color-background); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + color: var(--color-text); + } + + a { + color: var(--color-link); + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + a.external[target="_blank"] { + background-image: var(--external-icon); + background-position: top 3px right; + background-repeat: no-repeat; + padding-right: 13px; + } + a.tsd-anchor-link { + color: var(--color-text); + } + + code, + pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 0.875rem; + border-radius: 0.8em; + } + + pre { + position: relative; + white-space: pre-wrap; + word-wrap: break-word; + padding: 10px; + border: 1px solid var(--color-accent); + margin-bottom: 8px; + } + pre code { + padding: 0; + font-size: 100%; + } + pre > button { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.1s; + box-sizing: border-box; + } + pre:hover > button, + pre > button.visible { + opacity: 1; + } + + blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid gray; + } + + .tsd-typography { + line-height: 1.333em; + } + .tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; + } + .tsd-typography .tsd-index-panel h3, + .tsd-index-panel .tsd-typography h3, + .tsd-typography h4, + .tsd-typography h5, + .tsd-typography h6 { + font-size: 1em; + } + .tsd-typography h5, + .tsd-typography h6 { + font-weight: normal; + } + .tsd-typography p, + .tsd-typography ul, + .tsd-typography ol { + margin: 1em 0; + } + .tsd-typography table { + border-collapse: collapse; + border: none; + } + .tsd-typography td, + .tsd-typography th { + padding: 6px 13px; + border: 1px solid var(--color-accent); + } + .tsd-typography thead, + .tsd-typography tr:nth-child(even) { + background-color: var(--color-background-secondary); + } + + .tsd-alert { + padding: 8px 16px; + margin-bottom: 16px; + border-left: 0.25em solid var(--alert-color); + } + .tsd-alert blockquote > :last-child, + .tsd-alert > :last-child { + margin-bottom: 0; + } + .tsd-alert-title { + color: var(--alert-color); + display: inline-flex; + align-items: center; + } + .tsd-alert-title span { + margin-left: 4px; + } + + .tsd-alert-note { + --alert-color: var(--color-alert-note); + } + .tsd-alert-tip { + --alert-color: var(--color-alert-tip); + } + .tsd-alert-important { + --alert-color: var(--color-alert-important); + } + .tsd-alert-warning { + --alert-color: var(--color-alert-warning); + } + .tsd-alert-caution { + --alert-color: var(--color-alert-caution); + } + + .tsd-breadcrumb { + margin: 0; + padding: 0; + color: var(--color-text-aside); + } + .tsd-breadcrumb a { + color: var(--color-text-aside); + text-decoration: none; + } + .tsd-breadcrumb a:hover { + text-decoration: underline; + } + .tsd-breadcrumb li { + display: inline; + } + .tsd-breadcrumb li:after { + content: " / "; + } + + .tsd-comment-tags { + display: flex; + flex-direction: column; + } + dl.tsd-comment-tag-group { + display: flex; + align-items: center; + overflow: hidden; + margin: 0.5em 0; + } + dl.tsd-comment-tag-group dt { + display: flex; + margin-right: 0.5em; + font-size: 0.875em; + font-weight: normal; + } + dl.tsd-comment-tag-group dd { + margin: 0; + } + code.tsd-tag { + padding: 0.25em 0.4em; + border: 0.1em solid var(--color-accent); + margin-right: 0.25em; + font-size: 70%; + } + h1 code.tsd-tag:first-of-type { + margin-left: 0.25em; + } + + dl.tsd-comment-tag-group dd:before, + dl.tsd-comment-tag-group dd:after { + content: " "; + } + dl.tsd-comment-tag-group dd pre, + dl.tsd-comment-tag-group dd:after { + clear: both; + } + dl.tsd-comment-tag-group p { + margin: 0; + } + + .tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; + } + .tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; + } + + .tsd-filter-visibility h4 { + font-size: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.5rem; + margin: 0; + } + .tsd-filter-item:not(:last-child) { + margin-bottom: 0.5rem; + } + .tsd-filter-input { + display: flex; + width: -moz-fit-content; + width: fit-content; + align-items: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + } + .tsd-filter-input input[type="checkbox"] { + cursor: pointer; + position: absolute; + width: 1.5em; + height: 1.5em; + opacity: 0; + } + .tsd-filter-input input[type="checkbox"]:disabled { + pointer-events: none; + } + .tsd-filter-input svg { + cursor: pointer; + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; + border-radius: 0.33em; + /* Leaving this at full opacity breaks event listeners on Firefox. + Don't remove unless you know what you're doing. */ + opacity: 0.99; + } + .tsd-filter-input input[type="checkbox"]:focus-visible + svg { + outline: 2px solid var(--color-focus-outline); + } + .tsd-checkbox-background { + fill: var(--color-accent); + } + input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { + stroke: var(--color-text); + } + .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { + fill: var(--color-background); + stroke: var(--color-accent); + stroke-width: 0.25rem; + } + .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { + stroke: var(--color-accent); + } + + .settings-label { + font-weight: bold; + text-transform: uppercase; + display: inline-block; + } + + .tsd-filter-visibility .settings-label { + margin: 0.75rem 0 0.5rem 0; + } + + .tsd-theme-toggle .settings-label { + margin: 0.75rem 0.75rem 0 0; + } + + .tsd-hierarchy h4 label:hover span { + text-decoration: underline; + } + + .tsd-hierarchy { + list-style: square; + margin: 0; + } + .tsd-hierarchy-target { + font-weight: bold; + } + .tsd-hierarchy-toggle { + color: var(--color-link); + cursor: pointer; + } + + .tsd-full-hierarchy:not(:last-child) { + margin-bottom: 1em; + padding-bottom: 1em; + border-bottom: 1px solid var(--color-accent); + } + .tsd-full-hierarchy, + .tsd-full-hierarchy ul { + list-style: none; + margin: 0; + padding: 0; + } + .tsd-full-hierarchy ul { + padding-left: 1.5rem; + } + .tsd-full-hierarchy a { + padding: 0.25rem 0 !important; + font-size: 1rem; + display: inline-flex; + align-items: center; + color: var(--color-text); + } + .tsd-full-hierarchy svg[data-dropdown] { + cursor: pointer; + } + .tsd-full-hierarchy svg[data-dropdown="false"] { + transform: rotate(-90deg); + } + .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { + display: none; + } + + .tsd-panel-group.tsd-index-group { + margin-bottom: 0; + } + .tsd-index-panel .tsd-index-list { + list-style: none; + line-height: 1.333em; + margin: 0; + padding: 0.25rem 0 0 0; + overflow: hidden; + display: grid; + grid-template-columns: repeat(3, 1fr); + column-gap: 1rem; + grid-template-rows: auto; + } + @media (max-width: 1024px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(2, 1fr); + } + } + @media (max-width: 768px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(1, 1fr); + } + } + .tsd-index-panel .tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; + } + + .tsd-flag { + display: inline-block; + padding: 0.25em 0.4em; + border-radius: 4px; + color: var(--color-comment-tag-text); + background-color: var(--color-comment-tag); + text-indent: 0; + font-size: 75%; + line-height: 1; + font-weight: normal; + } + + .tsd-anchor { + position: relative; + top: -100px; + } + + .tsd-member { + position: relative; + } + .tsd-member .tsd-anchor + h3 { + display: flex; + align-items: center; + margin-top: 0; + margin-bottom: 0; + border-bottom: none; + } + + .tsd-navigation.settings { + margin: 1rem 0; + } + .tsd-navigation > a, + .tsd-navigation .tsd-accordion-summary { + width: calc(100% - 0.25rem); + display: flex; + align-items: center; + } + .tsd-navigation a, + .tsd-navigation summary > span, + .tsd-page-navigation a { + display: flex; + width: calc(100% - 0.25rem); + align-items: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + box-sizing: border-box; + } + .tsd-navigation a.current, + .tsd-page-navigation a.current { + background: var(--color-active-menu-item); + } + .tsd-navigation a:hover, + .tsd-page-navigation a:hover { + text-decoration: underline; + } + .tsd-navigation ul, + .tsd-page-navigation ul { + margin-top: 0; + margin-bottom: 0; + padding: 0; + list-style: none; + } + .tsd-navigation li, + .tsd-page-navigation li { + padding: 0; + max-width: 100%; + } + .tsd-navigation .tsd-nav-link { + display: none; + } + .tsd-nested-navigation { + margin-left: 3rem; + } + .tsd-nested-navigation > li > details { + margin-left: -1.5rem; + } + .tsd-small-nested-navigation { + margin-left: 1.5rem; + } + .tsd-small-nested-navigation > li > details { + margin-left: -1.5rem; + } + + .tsd-page-navigation-section { + margin-left: 10px; + } + .tsd-page-navigation-section > summary { + padding: 0.25rem; + } + .tsd-page-navigation-section > div { + margin-left: 20px; + } + .tsd-page-navigation ul { + padding-left: 1.75rem; + } + + #tsd-sidebar-links a { + margin-top: 0; + margin-bottom: 0.5rem; + line-height: 1.25rem; + } + #tsd-sidebar-links a:last-of-type { + margin-bottom: 0; + } + + a.tsd-index-link { + padding: 0.25rem 0 !important; + font-size: 1rem; + line-height: 1.25rem; + display: inline-flex; + align-items: center; + color: var(--color-text); + } + .tsd-accordion-summary { + list-style-type: none; /* hide marker on non-safari */ + outline: none; /* broken on safari, so just hide it */ + } + .tsd-accordion-summary::-webkit-details-marker { + display: none; /* hide marker on safari */ + } + .tsd-accordion-summary, + .tsd-accordion-summary a { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + + cursor: pointer; + } + .tsd-accordion-summary a { + width: calc(100% - 1.5rem); + } + .tsd-accordion-summary > * { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + } + .tsd-accordion .tsd-accordion-summary > svg { + margin-left: 0.25rem; + vertical-align: text-top; + } + /* + * We need to be careful to target the arrow indicating whether the accordion + * is open, but not any other SVGs included in the details element. + */ + .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child, + .tsd-accordion:not([open]) > .tsd-accordion-summary > h1 > svg:first-child, + .tsd-accordion:not([open]) > .tsd-accordion-summary > h2 > svg:first-child, + .tsd-accordion:not([open]) > .tsd-accordion-summary > h3 > svg:first-child, + .tsd-accordion:not([open]) > .tsd-accordion-summary > h4 > svg:first-child, + .tsd-accordion:not([open]) > .tsd-accordion-summary > h5 > svg:first-child { + transform: rotate(-90deg); + } + .tsd-index-content > :not(:first-child) { + margin-top: 0.75rem; + } + .tsd-index-heading { + margin-top: 1.5rem; + margin-bottom: 0.75rem; + } + + .tsd-no-select { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + .tsd-kind-icon { + margin-right: 0.5rem; + width: 1.25rem; + height: 1.25rem; + min-width: 1.25rem; + min-height: 1.25rem; + } + .tsd-signature > .tsd-kind-icon { + margin-right: 0.8rem; + } + + .tsd-panel { + margin-bottom: 2.5rem; + } + .tsd-panel.tsd-member { + margin-bottom: 4rem; + } + .tsd-panel:empty { + display: none; + } + .tsd-panel > h1, + .tsd-panel > h2, + .tsd-panel > h3 { + margin: 1.5rem -1.5rem 0.75rem -1.5rem; + padding: 0 1.5rem 0.75rem 1.5rem; + } + .tsd-panel > h1.tsd-before-signature, + .tsd-panel > h2.tsd-before-signature, + .tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: none; + } + + .tsd-panel-group { + margin: 2rem 0; + } + .tsd-panel-group.tsd-index-group { + margin: 2rem 0; + } + .tsd-panel-group.tsd-index-group details { + margin: 2rem 0; + } + .tsd-panel-group > .tsd-accordion-summary { + margin-bottom: 1rem; + } + + #tsd-search { + transition: background-color 0.2s; + } + #tsd-search .title { + position: relative; + z-index: 2; + } + #tsd-search .field { + position: absolute; + left: 0; + top: 0; + right: 2.5rem; + height: 100%; + } + #tsd-search .field input { + box-sizing: border-box; + position: relative; + top: -50px; + z-index: 1; + width: 100%; + padding: 0 10px; + opacity: 0; + outline: 0; + border: 0; + background: transparent; + color: var(--color-text); + } + #tsd-search .field label { + position: absolute; + overflow: hidden; + right: -40px; + } + #tsd-search .field input, + #tsd-search .title, + #tsd-toolbar-links a { + transition: opacity 0.2s; + } + #tsd-search .results { + position: absolute; + visibility: hidden; + top: 40px; + width: 100%; + margin: 0; + padding: 0; + list-style: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); + } + #tsd-search .results li { + background-color: var(--color-background); + line-height: initial; + padding: 4px; + } + #tsd-search .results li:nth-child(even) { + background-color: var(--color-background-secondary); + } + #tsd-search .results li.state { + display: none; + } + #tsd-search .results li.current:not(.no-results), + #tsd-search .results li:hover:not(.no-results) { + background-color: var(--color-accent); + } + #tsd-search .results a { + display: flex; + align-items: center; + padding: 0.25rem; + box-sizing: border-box; + } + #tsd-search .results a:before { + top: 10px; + } + #tsd-search .results span.parent { + color: var(--color-text-aside); + font-weight: normal; + } + #tsd-search.has-focus { + background-color: var(--color-accent); + } + #tsd-search.has-focus .field input { + top: 0; + opacity: 1; + } + #tsd-search.has-focus .title, + #tsd-search.has-focus #tsd-toolbar-links a { + z-index: 0; + opacity: 0; + } + #tsd-search.has-focus .results { + visibility: visible; + } + #tsd-search.loading .results li.state.loading { + display: block; + } + #tsd-search.failure .results li.state.failure { + display: block; + } + + #tsd-toolbar-links { + position: absolute; + top: 0; + right: 2rem; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + } + #tsd-toolbar-links a { + margin-left: 1.5rem; + } + #tsd-toolbar-links a:hover { + text-decoration: underline; + } + + .tsd-signature { + margin: 0 0 1rem 0; + padding: 1rem 0.5rem; + border: 1px solid var(--color-accent); + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; + overflow-x: auto; + } + + .tsd-signature-keyword { + color: var(--color-ts-keyword); + font-weight: normal; + } + + .tsd-signature-symbol { + color: var(--color-text-aside); + font-weight: normal; + } + + .tsd-signature-type { + font-style: italic; + font-weight: normal; + } + + .tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + list-style-type: none; + } + .tsd-signatures .tsd-signature { + margin: 0; + border-color: var(--color-accent); + border-width: 1px 0; + transition: background-color 0.1s; + } + .tsd-signatures .tsd-index-signature:not(:last-child) { + margin-bottom: 1em; + } + .tsd-signatures .tsd-index-signature .tsd-signature { + border-width: 1px; + } + .tsd-description .tsd-signatures .tsd-signature { + border-width: 1px; + } + + ul.tsd-parameter-list, + ul.tsd-type-parameter-list { + list-style: square; + margin: 0; + padding-left: 20px; + } + ul.tsd-parameter-list > li.tsd-parameter-signature, + ul.tsd-type-parameter-list > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; + } + ul.tsd-parameter-list h5, + ul.tsd-type-parameter-list h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; + } + .tsd-sources { + margin-top: 1rem; + font-size: 0.875em; + } + .tsd-sources a { + color: var(--color-text-aside); + text-decoration: underline; + } + .tsd-sources ul { + list-style: none; + padding: 0; + } + + .tsd-page-toolbar { + position: sticky; + z-index: 1; + top: 0; + left: 0; + width: 100%; + color: var(--color-text); + background: var(--color-background-secondary); + border-bottom: 1px var(--color-accent) solid; + transition: transform 0.3s ease-in-out; + } + .tsd-page-toolbar a { + color: var(--color-text); + text-decoration: none; + } + .tsd-page-toolbar a.title { + font-weight: bold; + } + .tsd-page-toolbar a.title:hover { + text-decoration: underline; + } + .tsd-page-toolbar .tsd-toolbar-contents { + display: flex; + justify-content: space-between; + height: 2.5rem; + margin: 0 auto; + } + .tsd-page-toolbar .table-cell { + position: relative; + white-space: nowrap; + line-height: 40px; + } + .tsd-page-toolbar .table-cell:first-child { + width: 100%; + } + .tsd-page-toolbar .tsd-toolbar-icon { + box-sizing: border-box; + line-height: 0; + padding: 12px 0; + } + + .tsd-widget { + display: inline-block; + overflow: hidden; + opacity: 0.8; + height: 40px; + transition: + opacity 0.1s, + background-color 0.2s; + vertical-align: bottom; + cursor: pointer; + } + .tsd-widget:hover { + opacity: 0.9; + } + .tsd-widget.active { + opacity: 1; + background-color: var(--color-accent); + } + .tsd-widget.no-caption { + width: 40px; + } + .tsd-widget.no-caption:before { + margin: 0; + } + + .tsd-widget.options, + .tsd-widget.menu { + display: none; + } + input[type="checkbox"] + .tsd-widget:before { + background-position: -120px 0; + } + input[type="checkbox"]:checked + .tsd-widget:before { + background-position: -160px 0; + } + + img { + max-width: 100%; + } + + .tsd-member-summary-name { + display: inline-flex; + align-items: center; + padding: 0.25rem; + text-decoration: none; + } + + .tsd-anchor-icon { + display: inline-flex; + align-items: center; + margin-left: 0.5rem; + color: var(--color-text); + } + + .tsd-anchor-icon svg { + width: 1em; + height: 1em; + visibility: hidden; + } + + .tsd-member-summary-name:hover > .tsd-anchor-icon svg, + .tsd-anchor-link:hover > .tsd-anchor-icon svg { + visibility: visible; + } + + .deprecated { + text-decoration: line-through !important; + } + + .warning { + padding: 1rem; + color: var(--color-warning-text); + background: var(--color-background-warning); + } + + .tsd-kind-project { + color: var(--color-ts-project); + } + .tsd-kind-module { + color: var(--color-ts-module); + } + .tsd-kind-namespace { + color: var(--color-ts-namespace); + } + .tsd-kind-enum { + color: var(--color-ts-enum); + } + .tsd-kind-enum-member { + color: var(--color-ts-enum-member); + } + .tsd-kind-variable { + color: var(--color-ts-variable); + } + .tsd-kind-function { + color: var(--color-ts-function); + } + .tsd-kind-class { + color: var(--color-ts-class); + } + .tsd-kind-interface { + color: var(--color-ts-interface); + } + .tsd-kind-constructor { + color: var(--color-ts-constructor); + } + .tsd-kind-property { + color: var(--color-ts-property); + } + .tsd-kind-method { + color: var(--color-ts-method); + } + .tsd-kind-reference { + color: var(--color-ts-reference); + } + .tsd-kind-call-signature { + color: var(--color-ts-call-signature); + } + .tsd-kind-index-signature { + color: var(--color-ts-index-signature); + } + .tsd-kind-constructor-signature { + color: var(--color-ts-constructor-signature); + } + .tsd-kind-parameter { + color: var(--color-ts-parameter); + } + .tsd-kind-type-parameter { + color: var(--color-ts-type-parameter); + } + .tsd-kind-accessor { + color: var(--color-ts-accessor); + } + .tsd-kind-get-signature { + color: var(--color-ts-get-signature); + } + .tsd-kind-set-signature { + color: var(--color-ts-set-signature); + } + .tsd-kind-type-alias { + color: var(--color-ts-type-alias); + } + + /* if we have a kind icon, don't color the text by kind */ + .tsd-kind-icon ~ span { + color: var(--color-text); + } + + * { + scrollbar-width: thin; + scrollbar-color: var(--color-accent) var(--color-icon-background); + } + + *::-webkit-scrollbar { + width: 0.75rem; + } + + *::-webkit-scrollbar-track { + background: var(--color-icon-background); + } + + *::-webkit-scrollbar-thumb { + background-color: var(--color-accent); + border-radius: 999rem; + border: 0.25rem solid var(--color-icon-background); + } + + /* mobile */ + @media (max-width: 769px) { + .tsd-widget.options, + .tsd-widget.menu { + display: inline-block; + } + + .container-main { + display: flex; + } + html .col-content { + float: none; + max-width: 100%; + width: 100%; + } + html .col-sidebar { + position: fixed !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + padding: 1.5rem 1.5rem 0 0; + width: 75vw; + visibility: hidden; + background-color: var(--color-background); + transform: translate(100%, 0); + } + html .col-sidebar > *:last-child { + padding-bottom: 20px; + } + html .overlay { + content: ""; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; + } + + .to-has-menu .overlay { + animation: fade-in 0.4s; + } + + .to-has-menu .col-sidebar { + animation: pop-in-from-right 0.4s; + } + + .from-has-menu .overlay { + animation: fade-out 0.4s; + } + + .from-has-menu .col-sidebar { + animation: pop-out-to-right 0.4s; + } + + .has-menu body { + overflow: hidden; + } + .has-menu .overlay { + visibility: visible; + } + .has-menu .col-sidebar { + visibility: visible; + transform: translate(0, 0); + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 100vh; + padding: 1rem 2rem; + } + .has-menu .tsd-navigation { + max-height: 100%; + } + #tsd-toolbar-links { + display: none; + } + .tsd-navigation .tsd-nav-link { + display: flex; + } + } + + /* one sidebar */ + @media (min-width: 770px) { + .container-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + grid-template-areas: "sidebar content"; + margin: 2rem auto; + } + + .col-sidebar { + grid-area: sidebar; + } + .col-content { + grid-area: content; + padding: 0 1rem; + } + } + @media (min-width: 770px) and (max-width: 1399px) { + .col-sidebar { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + padding-top: 1rem; + } + .site-menu { + margin-top: 1rem; + } + } + + /* two sidebars */ + @media (min-width: 1200px) { + .container-main { + grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax( + 0, + 20rem + ); + grid-template-areas: "sidebar content toc"; + } + + .col-sidebar { + display: contents; + } + + .page-menu { + grid-area: toc; + padding-left: 1rem; + } + .site-menu { + grid-area: sidebar; + } + + .site-menu { + margin-top: 1rem; + } + + .page-menu, + .site-menu { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + } + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/Call.RuntimeError.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/Call.RuntimeError.html new file mode 100644 index 000000000..c4e9ce71d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/Call.RuntimeError.html @@ -0,0 +1,11 @@ +RuntimeError | @wailsio/runtime

Exception class that will be thrown in case the bound method returns an error. +The value of the RuntimeError#name property is "RuntimeError".

+

Hierarchy

Constructors

Properties

Constructors

  • Constructs a new RuntimeError instance.

    +

    Parameters

    • Optionalmessage: string

      The error message.

      +
    • Optionaloptions: ErrorOptions

      Options to be forwarded to the Error constructor.

      +

    Returns RuntimeError

Properties

cause?: unknown
message: string
name: string
stack?: string
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancelError.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancelError.html new file mode 100644 index 000000000..07fd641d3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancelError.html @@ -0,0 +1,13 @@ +CancelError | @wailsio/runtime

Exception class that will be used as rejection reason +in case a CancellablePromise is cancelled successfully.

+

The value of the name property is the string "CancelError". +The value of the cause property is the cause passed to the cancel method, if any.

+

Hierarchy

Constructors

Properties

Constructors

Properties

cause?: unknown
message: string
name: string
stack?: string
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancellablePromise.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancellablePromise.html new file mode 100644 index 000000000..f90fae09d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancellablePromise.html @@ -0,0 +1,263 @@ +CancellablePromise | @wailsio/runtime

Class CancellablePromise<T>

A promise with an attached method for cancelling long-running operations (see CancellablePromise#cancel). +Cancellation can optionally be bound to an AbortSignal +for better composability (see CancellablePromise#cancelOn).

+

Cancelling a pending promise will result in an immediate rejection +with an instance of CancelError as reason, +but whoever started the promise will be responsible +for actually aborting the underlying operation. +To this purpose, the constructor and all chaining methods +accept optional cancellation callbacks.

+

If a CancellablePromise still resolves after having been cancelled, +the result will be discarded. If it rejects, the reason +will be reported as an unhandled rejection, +wrapped in a CancelledRejectionError instance. +To facilitate the handling of cancellation requests, +cancelled CancellablePromises will not report unhandled CancelErrors +whose cause field is the same as the one with which the current promise was cancelled.

+

All usual promise methods are defined and return a CancellablePromise +whose cancel method will cancel the parent operation as well, propagating the cancellation reason +upwards through promise chains. +Conversely, cancelling a promise will not automatically cancel dependent promises downstream:

+
let root = new CancellablePromise((resolve, reject) => { ... });
let child1 = root.then(() => { ... });
let child2 = child1.then(() => { ... });
let child3 = root.catch(() => { ... });
child1.cancel(); // Cancels child1 and root, but not child2 or child3 +
+ +

Cancelling a promise that has already settled is safe and has no consequence.

+

The cancel method returns a promise that always fulfills +after the whole chain has processed the cancel request +and all attached callbacks up to that moment have run.

+

All ES2024 promise methods (static and instance) are defined on CancellablePromise, +but actual availability may vary with OS/webview version.

+

In line with the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing, +CancellablePromise does not support transparent subclassing. +Extenders should take care to provide their own method implementations. +This might be reconsidered in case the proposal is retired.

+

CancellablePromise is a wrapper around the DOM Promise object +and is compliant with the Promises/A+ specification +(it passes the compliance suite) +if so is the underlying implementation.

+

Type Parameters

  • T

Hierarchy

Implements

Constructors

  • Creates a new CancellablePromise.

    +

    Type Parameters

    • T

    Parameters

    • executor: CancellablePromiseExecutor<T>

      A callback used to initialize the promise. This callback is passed two arguments: +a resolve callback used to resolve the promise with a value +or the result of another promise (possibly cancellable), +and a reject callback used to reject the promise with a provided reason or error. +If the value provided to the resolve callback is a thenable and cancellable object +(it has a then and a cancel method), +cancellation requests will be forwarded to that object and the oncancelled will not be invoked anymore. +If any one of the two callbacks is called after the promise has been cancelled, +the provided values will be cancelled and resolved as usual, +but their results will be discarded. +However, if the resolution process ultimately ends up in a rejection +that is not due to cancellation, the rejection reason +will be wrapped in a CancelledRejectionError +and bubbled up as an unhandled rejection.

      +
    • Optionaloncancelled: CancellablePromiseCanceller

      It is the caller's responsibility to ensure that any operation +started by the executor is properly halted upon cancellation. +This optional callback can be used to that purpose. +It will be called synchronously with a cancellation cause +when cancellation is requested, after the promise has already rejected +with a CancelError, but before +any then/catch/finally callback runs. +If the callback returns a thenable, the promise returned from cancel +will only fulfill after the former has settled. +Unhandled exceptions or rejections from the callback will be wrapped +in a CancelledRejectionError and bubbled up as unhandled rejections. +If the resolve callback is called before cancellation with a cancellable promise, +cancellation requests on this promise will be diverted to that promise, +and the original oncancelled callback will be discarded.

      +

    Returns CancellablePromise<T>

Properties

"[toStringTag]": string
"[species]": PromiseConstructor

Methods

  • Cancels immediately the execution of the operation associated with this promise. +The promise rejects with a CancelError instance as reason, +with the CancelError#cause property set to the given argument, if any.

    +

    Has no effect if called after the promise has already settled; +repeated calls in particular are safe, but only the first one +will set the cancellation cause.

    +

    The CancelError exception need not be handled explicitly on the promises that are being cancelled: +cancelling a promise with no attached rejection handler does not trigger an unhandled rejection event. +Therefore, the following idioms are all equally correct:

    +
    new CancellablePromise((resolve, reject) => { ... }).cancel();
    new CancellablePromise((resolve, reject) => { ... }).then(...).cancel();
    new CancellablePromise((resolve, reject) => { ... }).then(...).catch(...).cancel(); +
    + +

    Whenever some cancelled promise in a chain rejects with a CancelError +with the same cancellation cause as itself, the error will be discarded silently. +However, the CancelError will still be delivered to all attached rejection handlers +added by then and related methods:

    +
    let cancellable = new CancellablePromise((resolve, reject) => { ... });
    cancellable.then(() => { ... }).catch(console.log);
    cancellable.cancel(); // A CancelError is printed to the console. +
    + +

    If the CancelError is not handled downstream by the time it reaches +a non-cancelled promise, it will trigger an unhandled rejection event, +just like normal rejections would:

    +
    let cancellable = new CancellablePromise((resolve, reject) => { ... });
    let chained = cancellable.then(() => { ... }).then(() => { ... }); // No catch...
    cancellable.cancel(); // Unhandled rejection event on chained! +
    + +

    Therefore, it is important to either cancel whole promise chains from their tail, +as shown in the correct idioms above, or take care of handling errors everywhere.

    +

    Parameters

    • Optionalcause: any

    Returns CancellablePromise<void>

    A cancellable promise that fulfills after the cancel callback (if any) +and all handlers attached up to the call to cancel have run. +If the cancel callback returns a thenable, the promise returned by cancel +will also wait for that thenable to settle. +This enables callers to wait for the cancelled operation to terminate +without being forced to handle potential errors at the call site.

    +
    cancellable.cancel().then(() => {
    // Cleanup finished, it's safe to do something else.
    }, (err) => {
    // Unreachable: the promise returned from cancel will never reject.
    }); +
    + +

    Note that the returned promise will not handle implicitly any rejection +that might have occurred already in the cancelled chain. +It will just track whether registered handlers have been executed or not. +Therefore, unhandled rejections will never be silently handled by calling cancel.

    +
  • Binds promise cancellation to the abort event of the given AbortSignal. +If the signal has already aborted, the promise will be cancelled immediately. +When either condition is verified, the cancellation cause will be set +to the signal's abort reason (see AbortSignal.reason).

    +

    Has no effect if called (or if the signal aborts) after the promise has already settled. +Only the first signal to abort will set the cancellation cause.

    +

    For more details about the cancellation process, +see cancel and the CancellablePromise constructor.

    +

    This method enables awaiting cancellable promises without having +to store them for future cancellation, e.g.:

    +
    await longRunningOperation().cancelOn(signal);
    +
    + +

    instead of:

    +
    let promiseToBeCancelled = longRunningOperation();
    await promiseToBeCancelled; +
    + +

    Parameters

    Returns CancellablePromise<T>

    This promise, for method chaining.

    +
  • Attaches a callback for only the rejection of the Promise.

    +

    The optional oncancelled argument will be invoked when the returned promise is cancelled, +with the same semantics as the oncancelled argument of the constructor. +When the parent promise rejects or is cancelled, the onrejected callback will run, +even after the returned promise has been cancelled: +in that case, should it reject or throw, the reason will be wrapped +in a CancelledRejectionError and bubbled up as an unhandled rejection.

    +

    It is equivalent to

    +
    cancellablePromise.then(undefined, onrejected, oncancelled);
    +
    + +

    and the same caveats apply.

    +

    Type Parameters

    • TResult = never

    Parameters

    Returns CancellablePromise<T | TResult>

    A Promise for the completion of the callback. +Cancellation requests on the returned promise +will propagate up the chain to the parent promise, +but not in the other direction.

    +

    The promise returned from cancel will fulfill only after all attached handlers +up the entire promise chain have been run.

    +

    If onrejected returns a cancellable promise, +cancellation requests will be diverted to it, +and the specified oncancelled callback will be discarded. +See then for more details.

    +
  • Attaches a callback that is invoked when the CancellablePromise is settled (fulfilled or rejected). The +resolved value cannot be accessed or modified from the callback. +The returned promise will settle in the same state as the original one +after the provided callback has completed execution, +unless the callback throws or returns a rejecting promise, +in which case the returned promise will reject as well.

    +

    The optional oncancelled argument will be invoked when the returned promise is cancelled, +with the same semantics as the oncancelled argument of the constructor. +Once the parent promise settles, the onfinally callback will run, +even after the returned promise has been cancelled: +in that case, should it reject or throw, the reason will be wrapped +in a CancelledRejectionError and bubbled up as an unhandled rejection.

    +

    This method is implemented in terms of then and the same caveats apply. +It is polyfilled, hence available in every OS/webview version.

    +

    Parameters

    Returns CancellablePromise<T>

    A Promise for the completion of the callback. +Cancellation requests on the returned promise +will propagate up the chain to the parent promise, +but not in the other direction.

    +

    The promise returned from cancel will fulfill only after all attached handlers +up the entire promise chain have been run.

    +

    If onfinally returns a cancellable promise, +cancellation requests will be diverted to it, +and the specified oncancelled callback will be discarded. +See then for more details.

    +
  • Attaches callbacks for the resolution and/or rejection of the CancellablePromise.

    +

    The optional oncancelled argument will be invoked when the returned promise is cancelled, +with the same semantics as the oncancelled argument of the constructor. +When the parent promise rejects or is cancelled, the onrejected callback will run, +even after the returned promise has been cancelled: +in that case, should it reject or throw, the reason will be wrapped +in a CancelledRejectionError and bubbled up as an unhandled rejection.

    +

    Type Parameters

    • TResult1 = T
    • TResult2 = never

    Parameters

    Returns CancellablePromise<TResult1 | TResult2>

    A CancellablePromise for the completion of whichever callback is executed. +The returned promise is hooked up to propagate cancellation requests up the chain, but not down:

    +
      +
    • if the parent promise is cancelled, the onrejected handler will be invoked with a CancelError +and the returned promise will resolve regularly with its result;
    • +
    • conversely, if the returned promise is cancelled, the parent promise is cancelled too; +the onrejected handler will still be invoked with the parent's CancelError, +but its result will be discarded +and the returned promise will reject with a CancelError as well.
    • +
    +

    The promise returned from cancel will fulfill only after all attached handlers +up the entire promise chain have been run.

    +

    If either callback returns a cancellable promise, +cancellation requests will be diverted to it, +and the specified oncancelled callback will be discarded.

    +

Static Methods

  • Creates a CancellablePromise that is resolved with an array of results +when all of the provided Promises resolve, or rejected when any Promise is rejected.

    +

    Every one of the provided objects that is a thenable and cancellable object +will be cancelled when the returned promise is cancelled, with the same cause.

    +

    Type Parameters

    • T

    Parameters

    Returns CancellablePromise<Awaited<T>[]>

  • Creates a Promise that is resolved with an array of results when all of the provided Promises +resolve, or rejected when any Promise is rejected.

    +

    Type Parameters

    • T extends [] | readonly unknown[]

    Parameters

    • values: T

      An array of Promises.

      +

    Returns CancellablePromise<
        { -readonly [P in string
        | number
        | symbol]: Awaited<T[P<P>]> },
    >

    A new Promise.

    +
  • The any function returns a promise that is fulfilled by the first given promise to be fulfilled, +or rejected with an AggregateError containing an array of rejection reasons +if all of the given promises are rejected. +It resolves all elements of the passed iterable to promises as it runs this algorithm.

    +

    Every one of the provided objects that is a thenable and cancellable object +will be cancelled when the returned promise is cancelled, with the same cause.

    +

    Type Parameters

    • T

    Parameters

    Returns CancellablePromise<Awaited<T>>

  • The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError containing an array of rejection reasons if all of the given promises are rejected. It resolves all elements of the passed iterable to promises as it runs this algorithm.

    +

    Type Parameters

    • T extends [] | readonly unknown[]

    Parameters

    • values: T

      An array or iterable of Promises.

      +

    Returns CancellablePromise<Awaited<T[number]>>

    A new Promise.

    +
  • Creates a new CancellablePromise that resolves after the specified timeout. +The returned promise can be cancelled without consequences.

    +

    Parameters

    • milliseconds: number

    Returns CancellablePromise<void>

  • Creates a new CancellablePromise that resolves after +the specified timeout, with the provided value. +The returned promise can be cancelled without consequences.

    +

    Type Parameters

    • T

    Parameters

    • milliseconds: number
    • value: T

    Returns CancellablePromise<T>

  • Creates a new CancellablePromise that cancels +after the specified timeout, with the provided cause.

    +

    If the AbortSignal.timeout factory method is available, +it is used to base the timeout on active time rather than elapsed time. +Otherwise, timeout falls back to setTimeout.

    +

    Type Parameters

    • T = never

    Parameters

    • milliseconds: number
    • Optionalcause: any

    Returns CancellablePromise<T>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancelledRejectionError.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancelledRejectionError.html new file mode 100644 index 000000000..a059af22d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/CancelledRejectionError.html @@ -0,0 +1,21 @@ +CancelledRejectionError | @wailsio/runtime

Class CancelledRejectionError

Exception class that will be reported as an unhandled rejection +in case a CancellablePromise rejects after being cancelled, +or when the oncancelled callback throws or rejects.

+

The value of the name property is the string "CancelledRejectionError". +The value of the cause property is the reason the promise rejected with.

+

Because the original promise was cancelled, +a wrapper promise will be passed to the unhandled rejection listener instead. +The promise property holds a reference to the original promise.

+

Hierarchy

  • Error
    • CancelledRejectionError

Constructors

Properties

Constructors

  • Constructs a new CancelledRejectionError instance.

    +

    Parameters

    • promise: CancellablePromise<unknown>

      The promise that caused the error originally.

      +
    • Optionalreason: any

      The rejection reason.

      +
    • Optionalinfo: string

      An optional informative message specifying the circumstances in which the error was thrown. +Defaults to the string "Unhandled rejection in cancelled promise.".

      +

    Returns CancelledRejectionError

Properties

cause?: unknown
message: string
name: string
promise: CancellablePromise<unknown>

Holds a reference to the promise that was cancelled and then rejected.

+
stack?: string
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/Events.WailsEvent.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/Events.WailsEvent.html new file mode 100644 index 000000000..57ba9c59f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/Events.WailsEvent.html @@ -0,0 +1,10 @@ +WailsEvent | @wailsio/runtime

Represents a system event or a custom event emitted through wails-provided facilities.

+

Constructors

Properties

Constructors

Properties

data: any

Optional data associated with the emitted event.

+
name: string

The name of the event.

+
sender?: string

Name of the originating window. Omitted for application events. +Will be overridden if set manually.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/_internal_.Window.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/_internal_.Window.html new file mode 100644 index 000000000..71ee64115 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/classes/_internal_.Window.html @@ -0,0 +1,142 @@ +Window | @wailsio/runtime

Methods

  • Gets the specified window.

    +

    Parameters

    • name: string

      The name of the window to get.

      +

    Returns Window

    The corresponding window object.

    +
  • Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop). +Gathers information about the drop target element and sends it back to the Go backend.

    +

    Parameters

    • filenames: string[]

      An array of file paths (strings) that were dropped.

      +
    • x: number

      The x-coordinate of the drop event.

      +
    • y: number

      The y-coordinate of the drop event.

      +

    Returns void

  • Returns true if the window is focused.

    +

    Returns Promise<boolean>

    Whether the window is currently focused.

    +
  • Returns true if the window is fullscreen.

    +

    Returns Promise<boolean>

    Whether the window is currently fullscreen.

    +
  • Returns true if the window is maximised.

    +

    Returns Promise<boolean>

    Whether the window is currently maximised.

    +
  • Returns true if the window is minimised.

    +

    Returns Promise<boolean>

    Whether the window is currently minimised.

    +
  • Returns true if the window is resizable.

    +

    Returns Promise<boolean>

    Whether the window is currently resizable.

    +
  • Restores the window to its previous state if it was previously minimised, maximised or fullscreen.

    +

    Returns Promise<void>

  • Sets the window to be always on top.

    +

    Parameters

    • alwaysOnTop: boolean

      Whether the window should stay on top.

      +

    Returns Promise<void>

  • Sets the background colour of the window.

    +

    Parameters

    • r: number

      The desired red component of the window background.

      +
    • g: number

      The desired green component of the window background.

      +
    • b: number

      The desired blue component of the window background.

      +
    • a: number

      The desired alpha component of the window background.

      +

    Returns Promise<void>

  • Removes the window frame and title bar.

    +

    Parameters

    • frameless: boolean

      Whether the window should be frameless.

      +

    Returns Promise<void>

  • Disables the system fullscreen button.

    +

    Parameters

    • enabled: boolean

      Whether the fullscreen button should be enabled.

      +

    Returns Promise<void>

  • Sets the maximum size of the window.

    +

    Parameters

    • width: number

      The desired maximum width of the window.

      +
    • height: number

      The desired maximum height of the window.

      +

    Returns Promise<void>

  • Sets the minimum size of the window.

    +

    Parameters

    • width: number

      The desired minimum width of the window.

      +
    • height: number

      The desired minimum height of the window.

      +

    Returns Promise<void>

  • Sets the absolute position of the window.

    +

    Parameters

    • x: number

      The desired horizontal absolute position of the window.

      +
    • y: number

      The desired vertical absolute position of the window.

      +

    Returns Promise<void>

  • Sets the relative position of the window to the screen.

    +

    Parameters

    • x: number

      The desired horizontal relative position of the window.

      +
    • y: number

      The desired vertical relative position of the window.

      +

    Returns Promise<void>

  • Sets whether the window is resizable.

    +

    Parameters

    • resizable: boolean

      Whether the window should be resizable.

      +

    Returns Promise<void>

  • Sets the size of the window.

    +

    Parameters

    • width: number

      The desired width of the window.

      +
    • height: number

      The desired height of the window.

      +

    Returns Promise<void>

  • Sets the title of the window.

    +

    Parameters

    • title: string

      The desired title of the window.

      +

    Returns Promise<void>

  • Sets the zoom level of the window.

    +

    Parameters

    • zoom: number

      The desired zoom level.

      +

    Returns Promise<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Hide.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Hide.html new file mode 100644 index 000000000..4828abe86 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Hide.html @@ -0,0 +1,2 @@ +Hide | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Quit.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Quit.html new file mode 100644 index 000000000..bb3ec1cb1 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Quit.html @@ -0,0 +1,2 @@ +Quit | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Show.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Show.html new file mode 100644 index 000000000..83e6d02be --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Application.Show.html @@ -0,0 +1,2 @@ +Show | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Browser.OpenURL.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Browser.OpenURL.html new file mode 100644 index 000000000..c489dc6af --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Browser.OpenURL.html @@ -0,0 +1,3 @@ +OpenURL | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.ByID.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.ByID.html new file mode 100644 index 000000000..f155a72c4 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.ByID.html @@ -0,0 +1,6 @@ +ByID | @wailsio/runtime
  • Calls a method by its numeric ID with the specified arguments. +See Call for details.

    +

    Parameters

    • methodID: number

      The ID of the method to call.

      +
    • ...args: any[]

      The arguments to pass to the method.

      +

    Returns CancellablePromise<any>

    The result of the method call.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.ByName.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.ByName.html new file mode 100644 index 000000000..231587484 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.ByName.html @@ -0,0 +1,6 @@ +ByName | @wailsio/runtime
  • Calls a bound method by name with the specified arguments. +See Call for details.

    +

    Parameters

    • methodName: string

      The name of the method in the format 'package.struct.method'.

      +
    • ...args: any[]

      The arguments to pass to the method.

      +

    Returns CancellablePromise<any>

    The result of the method call.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.Call.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.Call.html new file mode 100644 index 000000000..48ed3e3a0 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Call.Call.html @@ -0,0 +1,9 @@ +Call | @wailsio/runtime
  • Call a bound method according to the given call options.

    +

    In case of failure, the returned promise will reject with an exception +among ReferenceError (unknown method), TypeError (wrong argument count or type), +RuntimeError (method returned an error), or other (network or internal errors). +The exception might have a "cause" field with the value returned +by the application- or service-level error marshaling functions.

    +

    Parameters

    Returns CancellablePromise<any>

    The result of the call.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Clipboard.SetText.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Clipboard.SetText.html new file mode 100644 index 000000000..681208368 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Clipboard.SetText.html @@ -0,0 +1,4 @@ +SetText | @wailsio/runtime
  • Sets the text to the Clipboard.

    +

    Parameters

    • text: string

      The text to be set to the Clipboard.

      +

    Returns Promise<void>

    A Promise that resolves when the operation is successful.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Clipboard.Text.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Clipboard.Text.html new file mode 100644 index 000000000..4e0514fe5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Clipboard.Text.html @@ -0,0 +1,3 @@ +Text | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Error.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Error.html new file mode 100644 index 000000000..1fbdcf82e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Error.html @@ -0,0 +1,4 @@ +Error | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Info.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Info.html new file mode 100644 index 000000000..e8606ad19 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Info.html @@ -0,0 +1,4 @@ +Info | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.OpenFile.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.OpenFile.html new file mode 100644 index 000000000..13ba2d544 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.OpenFile.html @@ -0,0 +1,10 @@ +OpenFile | @wailsio/runtime
  • Presents a file selection dialog to pick one or more files to open.

    +

    Parameters

    Returns Promise<string[]>

    Selected file or list of files, or a blank string/empty list if no file has been selected.

    +
  • Presents a file selection dialog to pick one or more files to open.

    +

    Parameters

    Returns Promise<string>

    Selected file or list of files, or a blank string/empty list if no file has been selected.

    +
  • Presents a file selection dialog to pick one or more files to open.

    +

    Parameters

    Returns Promise<string | string[]>

    Selected file or list of files, or a blank string/empty list if no file has been selected.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Question.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Question.html new file mode 100644 index 000000000..cb7ad090a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Question.html @@ -0,0 +1,4 @@ +Question | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.SaveFile.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.SaveFile.html new file mode 100644 index 000000000..7e2ec1103 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.SaveFile.html @@ -0,0 +1,4 @@ +SaveFile | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Warning.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Warning.html new file mode 100644 index 000000000..be1da407a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Dialogs.Warning.html @@ -0,0 +1,4 @@ +Warning | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Emit.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Emit.html new file mode 100644 index 000000000..472ec4758 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Emit.html @@ -0,0 +1,5 @@ +Emit | @wailsio/runtime
  • Emits an event using the name and data.

    +

    Parameters

    • name: string

      the name of the event to emit.

      +
    • Optionaldata: any

      the data to be sent with the event.

      +

    Returns Promise<void>

    A promise that will be fulfilled once the event has been emitted.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Off.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Off.html new file mode 100644 index 000000000..4be336e2a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Off.html @@ -0,0 +1,3 @@ +Off | @wailsio/runtime
  • Removes event listeners for the specified event names.

    +

    Parameters

    • ...eventNames: [string, ...string[]]

      The name of the events to remove listeners for.

      +

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.OffAll.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.OffAll.html new file mode 100644 index 000000000..e1015a79b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.OffAll.html @@ -0,0 +1,2 @@ +OffAll | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.On.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.On.html new file mode 100644 index 000000000..5d679cbd7 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.On.html @@ -0,0 +1,5 @@ +On | @wailsio/runtime
  • Registers a callback function to be executed when the specified event occurs.

    +

    Parameters

    • eventName: string

      The name of the event to register the callback for.

      +
    • callback: Callback

      The callback function to be called when the event is triggered.

      +

    Returns () => void

    A function that, when called, will unregister the callback from the event.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.OnMultiple.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.OnMultiple.html new file mode 100644 index 000000000..af4f37190 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.OnMultiple.html @@ -0,0 +1,6 @@ +OnMultiple | @wailsio/runtime
  • Register a callback function to be called multiple times for a specific event.

    +

    Parameters

    • eventName: string

      The name of the event to register the callback for.

      +
    • callback: Callback

      The callback function to be called when the event is triggered.

      +
    • maxCallbacks: number

      The maximum number of times the callback can be called for the event. Once the maximum number is reached, the callback will no longer be called.

      +

    Returns () => void

    A function that, when called, will unregister the callback from the event.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Once.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Once.html new file mode 100644 index 000000000..33bf9a888 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Events.Once.html @@ -0,0 +1,5 @@ +Once | @wailsio/runtime
  • Registers a callback function to be executed only once for the specified event.

    +

    Parameters

    • eventName: string

      The name of the event to register the callback for.

      +
    • callback: Callback

      The callback function to be called when the event is triggered.

      +

    Returns () => void

    A function that, when called, will unregister the callback from the event.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Flags.GetFlag.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Flags.GetFlag.html new file mode 100644 index 000000000..2c56d0f1b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Flags.GetFlag.html @@ -0,0 +1,4 @@ +GetFlag | @wailsio/runtime
  • Retrieves the value associated with the specified key from the flag map.

    +

    Parameters

    • key: string

      The key to retrieve the value for.

      +

    Returns any

    The value associated with the specified key.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetAll.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetAll.html new file mode 100644 index 000000000..45d99bb96 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetAll.html @@ -0,0 +1,3 @@ +GetAll | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetCurrent.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetCurrent.html new file mode 100644 index 000000000..b042871d5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetCurrent.html @@ -0,0 +1,3 @@ +GetCurrent | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetPrimary.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetPrimary.html new file mode 100644 index 000000000..a7bb9f8c7 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/Screens.GetPrimary.html @@ -0,0 +1,3 @@ +GetPrimary | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.Capabilities.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.Capabilities.html new file mode 100644 index 000000000..7188d4270 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.Capabilities.html @@ -0,0 +1,3 @@ +Capabilities | @wailsio/runtime
  • Fetches the capabilities of the application from the server.

    +

    Returns Promise<Record<string, any>>

    A promise that resolves to an object containing the capabilities.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.Environment.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.Environment.html new file mode 100644 index 000000000..732e6268b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.Environment.html @@ -0,0 +1,3 @@ +Environment | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.HandlePlatformFileDrop.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.HandlePlatformFileDrop.html new file mode 100644 index 000000000..03f7ecdc5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.HandlePlatformFileDrop.html @@ -0,0 +1,6 @@ +HandlePlatformFileDrop | @wailsio/runtime

Function HandlePlatformFileDrop

  • Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop). +Gathers information about the drop target element and sends it back to the Go backend.

    +

    Parameters

    • filenames: string[]

      An array of file paths (strings) that were dropped.

      +
    • x: number

      The x-coordinate of the drop event.

      +
    • y: number

      The y-coordinate of the drop event.

      +

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsAMD64.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsAMD64.html new file mode 100644 index 000000000..0968acc7b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsAMD64.html @@ -0,0 +1,3 @@ +IsAMD64 | @wailsio/runtime
  • Checks if the current environment architecture is AMD64.

    +

    Returns boolean

    True if the current environment architecture is AMD64, false otherwise.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsARM.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsARM.html new file mode 100644 index 000000000..dd266749e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsARM.html @@ -0,0 +1,3 @@ +IsARM | @wailsio/runtime
  • Checks if the current architecture is ARM.

    +

    Returns boolean

    True if the current architecture is ARM, false otherwise.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsARM64.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsARM64.html new file mode 100644 index 000000000..42cb38202 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsARM64.html @@ -0,0 +1,3 @@ +IsARM64 | @wailsio/runtime
  • Checks if the current environment is ARM64 architecture.

    +

    Returns boolean

    Returns true if the environment is ARM64 architecture, otherwise returns false.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsDarkMode.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsDarkMode.html new file mode 100644 index 000000000..57f20a869 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsDarkMode.html @@ -0,0 +1,3 @@ +IsDarkMode | @wailsio/runtime
  • Retrieves the system dark mode status.

    +

    Returns Promise<boolean>

    A promise that resolves to a boolean value indicating if the system is in dark mode.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsDebug.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsDebug.html new file mode 100644 index 000000000..a5e062c79 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsDebug.html @@ -0,0 +1,3 @@ +IsDebug | @wailsio/runtime
  • Reports whether the app is being run in debug mode.

    +

    Returns boolean

    True if the app is being run in debug mode.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsLinux.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsLinux.html new file mode 100644 index 000000000..24e0c4796 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsLinux.html @@ -0,0 +1,3 @@ +IsLinux | @wailsio/runtime
  • Checks if the current operating system is Linux.

    +

    Returns boolean

    Returns true if the current operating system is Linux, false otherwise.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsMac.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsMac.html new file mode 100644 index 000000000..24446433b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsMac.html @@ -0,0 +1,3 @@ +IsMac | @wailsio/runtime
  • Checks if the current environment is a macOS operating system.

    +

    Returns boolean

    True if the environment is macOS, false otherwise.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsWindows.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsWindows.html new file mode 100644 index 000000000..b7829d86b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.IsWindows.html @@ -0,0 +1,3 @@ +IsWindows | @wailsio/runtime
  • Checks if the current operating system is Windows.

    +

    Returns boolean

    True if the operating system is Windows, otherwise false.

    +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.invoke.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.invoke.html new file mode 100644 index 000000000..14c9978c0 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/System.invoke.html @@ -0,0 +1 @@ +invoke | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/WML.Enable.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/WML.Enable.html new file mode 100644 index 000000000..46bffbe7d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/WML.Enable.html @@ -0,0 +1,2 @@ +Enable | @wailsio/runtime
  • Schedules an automatic reload of WML to be performed as soon as the document is fully loaded.

    +

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/WML.Reload.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/WML.Reload.html new file mode 100644 index 000000000..41478c379 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/functions/WML.Reload.html @@ -0,0 +1,2 @@ +Reload | @wailsio/runtime
  • Reloads the WML page by adding necessary event listeners and browser listeners.

    +

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/hierarchy.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/hierarchy.html new file mode 100644 index 000000000..65c6d622b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/hierarchy.html @@ -0,0 +1 @@ +@wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/index.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/index.html new file mode 100644 index 000000000..90f345405 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/index.html @@ -0,0 +1,7 @@ +@wailsio/runtime

@wailsio/runtime

README

The index.js file in the compiled directory is the entrypoint for the runtime.js file that may be +loaded at runtime. This will add window.wails and window._wails to the global scope.

+

NOTE: It is preferable to use the @wailsio/runtime package to use the runtime.

+

⚠️ Do not rebuild the runtime manually after updating TS code: +the CI pipeline will take care of this. +PRs that touch build artifacts will be blocked from merging.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/CancellablePromiseLike.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/CancellablePromiseLike.html new file mode 100644 index 000000000..e7a8ca6f3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/CancellablePromiseLike.html @@ -0,0 +1,3 @@ +CancellablePromiseLike | @wailsio/runtime

Interface CancellablePromiseLike<T>

interface CancellablePromiseLike<T> {
    cancel(cause?: any): void | PromiseLike<void>;
    then<TResult1 = T, TResult2 = never>(
        onfulfilled?:
            | null
            | (
                value: T,
            ) => TResult1 | PromiseLike<TResult1> | CancellablePromiseLike<TResult1>,
        onrejected?:
            | null
            | (
                reason: any,
            ) => TResult2 | PromiseLike<TResult2> | CancellablePromiseLike<TResult2>,
    ): CancellablePromiseLike<TResult1 | TResult2>;
}

Type Parameters

  • T

Implemented by

Methods

Methods

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/CancellablePromiseWithResolvers.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/CancellablePromiseWithResolvers.html new file mode 100644 index 000000000..3bd162eae --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/CancellablePromiseWithResolvers.html @@ -0,0 +1,7 @@ +CancellablePromiseWithResolvers | @wailsio/runtime

Interface CancellablePromiseWithResolvers<T>

Wraps a cancellable promise along with its resolution methods. +The oncancelled field will be null initially but may be set to provide a custom cancellation function.

+
interface CancellablePromiseWithResolvers<T> {
    oncancelled: null | CancellablePromiseCanceller;
    promise: CancellablePromise<T>;
    reject: CancellablePromiseRejector;
    resolve: CancellablePromiseResolver<T>;
}

Type Parameters

  • T

Properties

oncancelled: null | CancellablePromiseCanceller
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.Button.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.Button.html new file mode 100644 index 000000000..925410339 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.Button.html @@ -0,0 +1,7 @@ +Button | @wailsio/runtime
interface Button {
    IsCancel?: boolean;
    IsDefault?: boolean;
    Label?: string;
}

Properties

IsCancel?: boolean

True if the button should cancel an operation when clicked.

+
IsDefault?: boolean

True if the button should be the default action when the user presses enter.

+
Label?: string

Text that appears within the button.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.FileFilter.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.FileFilter.html new file mode 100644 index 000000000..09958a9cd --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.FileFilter.html @@ -0,0 +1,5 @@ +FileFilter | @wailsio/runtime
interface FileFilter {
    DisplayName?: string;
    Pattern?: string;
}

Properties

Properties

DisplayName?: string

Display name for the filter, it could be "Text Files", "Images" etc.

+
Pattern?: string

Pattern to match for the filter, e.g. ".txt;.md" for text markdown files.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.MessageDialogOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.MessageDialogOptions.html new file mode 100644 index 000000000..27ccf9d7a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.MessageDialogOptions.html @@ -0,0 +1,9 @@ +MessageDialogOptions | @wailsio/runtime

Interface MessageDialogOptions

interface MessageDialogOptions {
    Buttons?: Button[];
    Detached?: boolean;
    Message?: string;
    Title?: string;
}

Properties

Buttons?: Button[]

Array of button options to show in the dialog.

+
Detached?: boolean

True if the dialog should appear detached from the main window (if applicable).

+
Message?: string

The main message to show in the dialog.

+
Title?: string

The title of the dialog window.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.OpenFileDialogOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.OpenFileDialogOptions.html new file mode 100644 index 000000000..3dd409a4f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.OpenFileDialogOptions.html @@ -0,0 +1,33 @@ +OpenFileDialogOptions | @wailsio/runtime

Interface OpenFileDialogOptions

interface OpenFileDialogOptions {
    AllowsMultipleSelection?: boolean;
    AllowsOtherFiletypes?: boolean;
    ButtonText?: string;
    CanChooseDirectories?: boolean;
    CanChooseFiles?: boolean;
    CanCreateDirectories?: boolean;
    CanSelectHiddenExtension?: boolean;
    Detached?: boolean;
    Directory?: string;
    Filters?: FileFilter[];
    HideExtension?: boolean;
    Message?: string;
    ResolvesAliases?: boolean;
    ShowHiddenFiles?: boolean;
    Title?: string;
    TreatsFilePackagesAsDirectories?: boolean;
}

Properties

AllowsMultipleSelection?: boolean

Indicates if multiple selection is allowed.

+
AllowsOtherFiletypes?: boolean

Indicates if other file types are allowed.

+
ButtonText?: string

Text to display on the button.

+
CanChooseDirectories?: boolean

Indicates if directories can be chosen.

+
CanChooseFiles?: boolean

Indicates if files can be chosen.

+
CanCreateDirectories?: boolean

Indicates if directories can be created.

+
CanSelectHiddenExtension?: boolean

Indicates if hidden extensions can be selected.

+
Detached?: boolean

Indicates if the dialog should appear detached from the main window.

+
Directory?: string

Directory to open in the dialog.

+
Filters?: FileFilter[]

Array of file filters.

+
HideExtension?: boolean

Indicates if the extension should be hidden.

+
Message?: string

Message to show in the dialog.

+
ResolvesAliases?: boolean

Indicates if aliases should be resolved.

+
ShowHiddenFiles?: boolean

Indicates if hidden files should be shown.

+
Title?: string

Title of the dialog.

+
TreatsFilePackagesAsDirectories?: boolean

Indicates if file packages should be treated as directories.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.SaveFileDialogOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.SaveFileDialogOptions.html new file mode 100644 index 000000000..6752aefc3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Dialogs.SaveFileDialogOptions.html @@ -0,0 +1,33 @@ +SaveFileDialogOptions | @wailsio/runtime

Interface SaveFileDialogOptions

interface SaveFileDialogOptions {
    AllowsOtherFiletypes?: boolean;
    ButtonText?: string;
    CanChooseDirectories?: boolean;
    CanChooseFiles?: boolean;
    CanCreateDirectories?: boolean;
    CanSelectHiddenExtension?: boolean;
    Detached?: boolean;
    Directory?: string;
    Filename?: string;
    Filters?: FileFilter[];
    HideExtension?: boolean;
    Message?: string;
    ResolvesAliases?: boolean;
    ShowHiddenFiles?: boolean;
    Title?: string;
    TreatsFilePackagesAsDirectories?: boolean;
}

Properties

AllowsOtherFiletypes?: boolean

Indicates if other file types are allowed.

+
ButtonText?: string

Text to display on the button.

+
CanChooseDirectories?: boolean

Indicates if directories can be chosen.

+
CanChooseFiles?: boolean

Indicates if files can be chosen.

+
CanCreateDirectories?: boolean

Indicates if directories can be created.

+
CanSelectHiddenExtension?: boolean

Indicates if hidden extensions can be selected.

+
Detached?: boolean

Indicates if the dialog should appear detached from the main window.

+
Directory?: string

Directory to open in the dialog.

+
Filename?: string

Default filename to use in the dialog.

+
Filters?: FileFilter[]

Array of file filters.

+
HideExtension?: boolean

Indicates if the extension should be hidden.

+
Message?: string

Message to show in the dialog.

+
ResolvesAliases?: boolean

Indicates if aliases should be resolved.

+
ShowHiddenFiles?: boolean

Indicates if hidden files should be shown.

+
Title?: string

Title of the dialog.

+
TreatsFilePackagesAsDirectories?: boolean

Indicates if file packages should be treated as directories.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Rect.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Rect.html new file mode 100644 index 000000000..3c3b36416 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Rect.html @@ -0,0 +1,9 @@ +Rect | @wailsio/runtime
interface Rect {
    Height: number;
    Width: number;
    X: number;
    Y: number;
}

Properties

Properties

Height: number

The height of the rectangle.

+
Width: number

The width of the rectangle.

+
X: number

The X coordinate of the origin.

+
Y: number

The Y coordinate of the origin.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Screen.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Screen.html new file mode 100644 index 000000000..78e0df769 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Screen.html @@ -0,0 +1,25 @@ +Screen | @wailsio/runtime
interface Screen {
    Bounds: Rect;
    ID: string;
    IsPrimary: boolean;
    Name: string;
    PhysicalBounds: Rect;
    PhysicalWorkArea: Rect;
    Rotation: number;
    ScaleFactor: number;
    Size: Screens.Size;
    WorkArea: Rect;
    X: number;
    Y: number;
}

Properties

Bounds: Rect

Contains the bounds of the screen in terms of X, Y, Width, and Height.

+
ID: string

Unique identifier for the screen.

+
IsPrimary: boolean

True if this is the primary monitor selected by the user in the operating system.

+
Name: string

Human-readable name of the screen.

+
PhysicalBounds: Rect

Contains the physical bounds of the screen in terms of X, Y, Width, and Height (before scaling).

+
PhysicalWorkArea: Rect

Contains the physical WorkArea of the screen (before scaling).

+
Rotation: number

The rotation of the screen.

+
ScaleFactor: number

The scale factor of the screen (DPI/96). 1 = standard DPI, 2 = HiDPI (Retina), etc.

+

Contains the width and height of the screen.

+
WorkArea: Rect

Contains the area of the screen that is actually usable (excluding taskbar and other system UI).

+
X: number

The X coordinate of the screen.

+
Y: number

The Y coordinate of the screen.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Size.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Size.html new file mode 100644 index 000000000..03f72fcaf --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/Screens.Size.html @@ -0,0 +1,5 @@ +Size | @wailsio/runtime
interface Size {
    Height: number;
    Width: number;
}

Properties

Properties

Height: number

The height of a rectangular area.

+
Width: number

The width of a rectangular area.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/System.EnvironmentInfo.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/System.EnvironmentInfo.html new file mode 100644 index 000000000..6cd10fa34 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/System.EnvironmentInfo.html @@ -0,0 +1,11 @@ +EnvironmentInfo | @wailsio/runtime

Interface EnvironmentInfo

interface EnvironmentInfo {
    Arch: string;
    Debug: boolean;
    OS: string;
    OSInfo: OSInfo;
    PlatformInfo: Record<string, any>;
}

Properties

Properties

Arch: string

The architecture of the system.

+
Debug: boolean

True if the application is running in debug mode, otherwise false.

+
OS: string

The operating system in use.

+
OSInfo: OSInfo

Details of the operating system.

+
PlatformInfo: Record<string, any>

Additional platform information.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/System.OSInfo.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/System.OSInfo.html new file mode 100644 index 000000000..03f29b61f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/System.OSInfo.html @@ -0,0 +1,9 @@ +OSInfo | @wailsio/runtime
interface OSInfo {
    Branding: string;
    ID: string;
    Name: string;
    Version: string;
}

Properties

Properties

Branding: string

The branding of the OS.

+
ID: string

The ID of the OS.

+
Name: string

The name of the OS.

+
Version: string

The version of the OS.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.AddEventListenerOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.AddEventListenerOptions.html new file mode 100644 index 000000000..3dd0acbc4 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.AddEventListenerOptions.html @@ -0,0 +1,5 @@ +AddEventListenerOptions | @wailsio/runtime

Interface AddEventListenerOptions

interface AddEventListenerOptions {
    capture?: boolean;
    once?: boolean;
    passive?: boolean;
    signal?: AbortSignal;
}

Hierarchy (View Summary)

Properties

capture?: boolean
once?: boolean
passive?: boolean
signal?: AbortSignal
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ArrayBufferView.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ArrayBufferView.html new file mode 100644 index 000000000..34cc95d72 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ArrayBufferView.html @@ -0,0 +1,7 @@ +ArrayBufferView | @wailsio/runtime

Interface ArrayBufferView<TArrayBuffer>

interface ArrayBufferView<
    TArrayBuffer extends ArrayBufferLike = ArrayBufferLike,
> {
    buffer: TArrayBuffer;
    byteLength: number;
    byteOffset: number;
}

Type Parameters

Properties

buffer: TArrayBuffer

The ArrayBuffer instance referenced by the array.

+
byteLength: number

The length in bytes of the array.

+
byteOffset: number

The offset in bytes of the array.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Blob.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Blob.html new file mode 100644 index 000000000..051fea2a3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Blob.html @@ -0,0 +1,17 @@ +Blob | @wailsio/runtime

A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system.

+

MDN Reference

+
interface Blob {
    size: number;
    type: string;
    arrayBuffer(): Promise<ArrayBuffer>;
    bytes(): Promise<Uint8Array<ArrayBufferLike>>;
    slice(start?: number, end?: number, contentType?: string): Blob;
    stream(): ReadableStream<Uint8Array<ArrayBufferLike>>;
    text(): Promise<string>;
}

Properties

Methods

Properties

size: number
type: string

Methods

  • Parameters

    • Optionalstart: number
    • Optionalend: number
    • OptionalcontentType: string

    Returns Blob

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.BlobPropertyBag.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.BlobPropertyBag.html new file mode 100644 index 000000000..6fdfdc2d7 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.BlobPropertyBag.html @@ -0,0 +1,3 @@ +BlobPropertyBag | @wailsio/runtime
interface BlobPropertyBag {
    endings?: EndingType;
    type?: string;
}

Properties

Properties

endings?: EndingType
type?: string
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ErrorOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ErrorOptions.html new file mode 100644 index 000000000..12c1c1dec --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ErrorOptions.html @@ -0,0 +1,2 @@ +ErrorOptions | @wailsio/runtime
interface ErrorOptions {
    cause?: unknown;
}

Properties

Properties

cause?: unknown
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Event.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Event.html new file mode 100644 index 000000000..658cc399a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Event.html @@ -0,0 +1,57 @@ +Event | @wailsio/runtime

An event which takes place in the DOM.

+

MDN Reference

+
interface Event {
    AT_TARGET: 2;
    bubbles: boolean;
    BUBBLING_PHASE: 3;
    cancelable: boolean;
    cancelBubble: boolean;
    CAPTURING_PHASE: 1;
    composed: boolean;
    currentTarget: null | EventTarget;
    defaultPrevented: boolean;
    eventPhase: number;
    isTrusted: boolean;
    NONE: 0;
    returnValue: boolean;
    srcElement: null | EventTarget;
    target: null | EventTarget;
    timeStamp: number;
    type: string;
    composedPath(): EventTarget[];
    initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
    preventDefault(): void;
    stopImmediatePropagation(): void;
    stopPropagation(): void;
}

Properties

AT_TARGET: 2
bubbles: boolean

Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise.

+

MDN Reference

+
BUBBLING_PHASE: 3
cancelable: boolean

Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method.

+

MDN Reference

+
cancelBubble: boolean

MDN Reference

+
CAPTURING_PHASE: 1
composed: boolean

Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise.

+

MDN Reference

+
currentTarget: null | EventTarget

Returns the object whose event listener's callback is currently being invoked.

+

MDN Reference

+
defaultPrevented: boolean

Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise.

+

MDN Reference

+
eventPhase: number

Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE.

+

MDN Reference

+
isTrusted: boolean

Returns true if event was dispatched by the user agent, and false otherwise.

+

MDN Reference

+
NONE: 0
returnValue: boolean

MDN Reference

+
srcElement: null | EventTarget

MDN Reference

+
target: null | EventTarget

Returns the object to which event is dispatched (its target).

+

MDN Reference

+
timeStamp: number

Returns the event's timestamp as the number of milliseconds measured relative to the time origin.

+

MDN Reference

+
type: string

Returns the type of event, e.g. "click", "hashchange", or "submit".

+

MDN Reference

+

Methods

  • Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget.

    +

    MDN Reference

    +

    Returns EventTarget[]

  • Parameters

    • type: string
    • Optionalbubbles: boolean
    • Optionalcancelable: boolean

    Returns void

    MDN Reference

    +
  • If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled.

    +

    MDN Reference

    +

    Returns void

  • Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects.

    +

    MDN Reference

    +

    Returns void

  • When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object.

    +

    MDN Reference

    +

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventInit.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventInit.html new file mode 100644 index 000000000..27cc94ba8 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventInit.html @@ -0,0 +1,4 @@ +EventInit | @wailsio/runtime
interface EventInit {
    bubbles?: boolean;
    cancelable?: boolean;
    composed?: boolean;
}

Properties

bubbles?: boolean
cancelable?: boolean
composed?: boolean
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListener.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListener.html new file mode 100644 index 000000000..aa2f55376 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListener.html @@ -0,0 +1 @@ +EventListener | @wailsio/runtime
  • Parameters

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListenerObject.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListenerObject.html new file mode 100644 index 000000000..c644929fd --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListenerObject.html @@ -0,0 +1,2 @@ +EventListenerObject | @wailsio/runtime
interface EventListenerObject {
    handleEvent(object: Event): void;
}

Methods

Methods

  • Parameters

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListenerOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListenerOptions.html new file mode 100644 index 000000000..fe81f58eb --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventListenerOptions.html @@ -0,0 +1,2 @@ +EventListenerOptions | @wailsio/runtime
interface EventListenerOptions {
    capture?: boolean;
}

Hierarchy (View Summary)

Properties

Properties

capture?: boolean
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventTarget.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventTarget.html new file mode 100644 index 000000000..db486f12f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.EventTarget.html @@ -0,0 +1,18 @@ +EventTarget | @wailsio/runtime

EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them.

+

MDN Reference

+
interface EventTarget {
    addEventListener(
        type: string,
        callback: null | EventListenerOrEventListenerObject,
        options?: boolean | AddEventListenerOptions,
    ): void;
    dispatchEvent(event: Event): boolean;
    removeEventListener(
        type: string,
        callback: null | EventListenerOrEventListenerObject,
        options?: boolean | EventListenerOptions,
    ): void;
}

Hierarchy (View Summary)

Methods

  • Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

    +

    The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture.

    +

    When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET.

    +

    When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

    +

    When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed.

    +

    If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted.

    +

    The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture.

    +

    MDN Reference

    +

    Parameters

    Returns void

  • Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.

    +

    MDN Reference

    +

    Parameters

    Returns boolean

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Iterable.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Iterable.html new file mode 100644 index 000000000..02cc4cf80 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Iterable.html @@ -0,0 +1,2 @@ +Iterable | @wailsio/runtime

Interface Iterable<T, TReturn, TNext>

interface Iterable<T, TReturn = any, TNext = any> {
    "[iterator]"(): Iterator<T, TReturn, TNext>;
}

Type Parameters

  • T
  • TReturn = any
  • TNext = any

Methods

Methods

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.MediaSource.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.MediaSource.html new file mode 100644 index 000000000..ebd122f16 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.MediaSource.html @@ -0,0 +1,49 @@ +MediaSource | @wailsio/runtime

This Media Source Extensions API interface represents a source of media data for an HTMLMediaElement object. A MediaSource object can be attached to a HTMLMediaElement to be played in the user agent.

+

MDN Reference

+
interface MediaSource {
    activeSourceBuffers: SourceBufferList;
    duration: number;
    onsourceclose: null | (this: MediaSource, ev: Event) => any;
    onsourceended: null | (this: MediaSource, ev: Event) => any;
    onsourceopen: null | (this: MediaSource, ev: Event) => any;
    readyState: ReadyState;
    sourceBuffers: SourceBufferList;
    addEventListener<K extends keyof MediaSourceEventMap>(
        type: K,
        listener: (this: MediaSource, ev: MediaSourceEventMap[K]) => any,
        options?: boolean | AddEventListenerOptions,
    ): void;
    addEventListener(
        type: string,
        listener: EventListenerOrEventListenerObject,
        options?: boolean | AddEventListenerOptions,
    ): void;
    addSourceBuffer(type: string): SourceBuffer;
    clearLiveSeekableRange(): void;
    dispatchEvent(event: Event): boolean;
    endOfStream(error?: EndOfStreamError): void;
    removeEventListener<K extends keyof MediaSourceEventMap>(
        type: K,
        listener: (this: MediaSource, ev: MediaSourceEventMap[K]) => any,
        options?: boolean | EventListenerOptions,
    ): void;
    removeEventListener(
        type: string,
        listener: EventListenerOrEventListenerObject,
        options?: boolean | EventListenerOptions,
    ): void;
    removeSourceBuffer(sourceBuffer: SourceBuffer): void;
    setLiveSeekableRange(start: number, end: number): void;
}

Hierarchy (View Summary)

Properties

activeSourceBuffers: SourceBufferList
duration: number
onsourceclose: null | (this: MediaSource, ev: Event) => any
onsourceended: null | (this: MediaSource, ev: Event) => any
onsourceopen: null | (this: MediaSource, ev: Event) => any
readyState: ReadyState
sourceBuffers: SourceBufferList

Methods

  • Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

    +

    The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture.

    +

    When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET.

    +

    When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

    +

    When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed.

    +

    If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted.

    +

    The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture.

    +

    MDN Reference

    +

    Type Parameters

    Parameters

    Returns void

  • Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

    +

    The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture.

    +

    When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET.

    +

    When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

    +

    When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed.

    +

    If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted.

    +

    The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture.

    +

    MDN Reference

    +

    Parameters

    Returns void

  • Returns void

  • Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.

    +

    MDN Reference

    +

    Parameters

    Returns boolean

  • Parameters

    • start: number
    • end: number

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.MediaSourceEventMap.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.MediaSourceEventMap.html new file mode 100644 index 000000000..ece981957 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.MediaSourceEventMap.html @@ -0,0 +1,4 @@ +MediaSourceEventMap | @wailsio/runtime
interface MediaSourceEventMap {
    sourceclose: Event;
    sourceended: Event;
    sourceopen: Event;
}

Properties

sourceclose: Event
sourceended: Event
sourceopen: Event
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Position.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Position.html new file mode 100644 index 000000000..72535066f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Position.html @@ -0,0 +1,6 @@ +Position | @wailsio/runtime

A record describing the position of a window.

+
interface Position {
    x: number;
    y: number;
}

Properties

x +y +

Properties

x: number

The horizontal position of the window.

+
y: number

The vertical position of the window.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseFulfilledResult.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseFulfilledResult.html new file mode 100644 index 000000000..0f1002ee8 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseFulfilledResult.html @@ -0,0 +1,3 @@ +PromiseFulfilledResult | @wailsio/runtime

Interface PromiseFulfilledResult<T>

interface PromiseFulfilledResult<T> {
    status: "fulfilled";
    value: T;
}

Type Parameters

  • T

Properties

Properties

status: "fulfilled"
value: T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseLike.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseLike.html new file mode 100644 index 000000000..4add9f58e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseLike.html @@ -0,0 +1,6 @@ +PromiseLike | @wailsio/runtime
interface PromiseLike<T> {
    then<TResult1 = T, TResult2 = never>(
        onfulfilled?: null | (value: T) => TResult1 | PromiseLike<TResult1>,
        onrejected?: null | (reason: any) => TResult2 | PromiseLike<TResult2>,
    ): PromiseLike<TResult1 | TResult2>;
}

Type Parameters

  • T

Implemented by

Methods

Methods

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseRejectedResult.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseRejectedResult.html new file mode 100644 index 000000000..a364e257f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseRejectedResult.html @@ -0,0 +1,3 @@ +PromiseRejectedResult | @wailsio/runtime
interface PromiseRejectedResult {
    reason: any;
    status: "rejected";
}

Properties

Properties

reason: any
status: "rejected"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseWithResolvers.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseWithResolvers.html new file mode 100644 index 000000000..b942f497b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.PromiseWithResolvers.html @@ -0,0 +1,4 @@ +PromiseWithResolvers | @wailsio/runtime

Interface PromiseWithResolvers<T>

interface PromiseWithResolvers<T> {
    promise: Promise<T>;
    reject: (reason?: any) => void;
    resolve: (value: T | PromiseLike<T>) => void;
}

Type Parameters

  • T

Properties

Properties

promise: Promise<T>
reject: (reason?: any) => void
resolve: (value: T | PromiseLike<T>) => void
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.QueuingStrategy.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.QueuingStrategy.html new file mode 100644 index 000000000..149c55d8c --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.QueuingStrategy.html @@ -0,0 +1,3 @@ +QueuingStrategy | @wailsio/runtime

Interface QueuingStrategy<T>

interface QueuingStrategy<T = any> {
    highWaterMark?: number;
    size?: QueuingStrategySize<T>;
}

Type Parameters

  • T = any

Properties

Properties

highWaterMark?: number
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.QueuingStrategySize.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.QueuingStrategySize.html new file mode 100644 index 000000000..5f653c14e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.QueuingStrategySize.html @@ -0,0 +1 @@ +QueuingStrategySize | @wailsio/runtime

Interface QueuingStrategySize<T>

Type Parameters

  • T = any
  • Parameters

    • chunk: T

    Returns number

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableByteStreamController.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableByteStreamController.html new file mode 100644 index 000000000..5dce85cc7 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableByteStreamController.html @@ -0,0 +1,12 @@ +ReadableByteStreamController | @wailsio/runtime

Interface ReadableByteStreamController

interface ReadableByteStreamController {
    byobRequest: null | ReadableStreamBYOBRequest;
    desiredSize: null | number;
    close(): void;
    enqueue(chunk: ArrayBufferView): void;
    error(e?: any): void;
}

Properties

Methods

Properties

byobRequest: null | ReadableStreamBYOBRequest
desiredSize: null | number

Methods

  • Returns void

  • Parameters

    • Optionale: any

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStream.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStream.html new file mode 100644 index 000000000..b7d1939f3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStream.html @@ -0,0 +1,15 @@ +ReadableStream | @wailsio/runtime

Interface ReadableStream<R>

This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object.

+

MDN Reference

+
interface ReadableStream<R = any> {
    locked: boolean;
    cancel(reason?: any): Promise<void>;
    getReader(options: { mode: "byob" }): ReadableStreamBYOBReader;
    getReader(): ReadableStreamDefaultReader<R>;
    getReader(
        options?: ReadableStreamGetReaderOptions,
    ): ReadableStreamReader<R>;
    pipeThrough<T>(
        transform: ReadableWritablePair<T, R>,
        options?: StreamPipeOptions,
    ): ReadableStream<T>;
    pipeTo(
        destination: WritableStream<R>,
        options?: StreamPipeOptions,
    ): Promise<void>;
    tee(): [ReadableStream<R>, ReadableStream<R>];
}

Type Parameters

  • R = any

Properties

Methods

Properties

locked: boolean

Methods

  • Parameters

    • Optionalreason: any

    Returns Promise<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamBYOBReader.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamBYOBReader.html new file mode 100644 index 000000000..99ffeb14a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamBYOBReader.html @@ -0,0 +1,10 @@ +ReadableStreamBYOBReader | @wailsio/runtime

Interface ReadableStreamBYOBReader

interface ReadableStreamBYOBReader {
    closed: Promise<undefined>;
    cancel(reason?: any): Promise<void>;
    read<T extends ArrayBufferView<ArrayBufferLike>>(
        view: T,
    ): Promise<ReadableStreamReadResult<T>>;
    releaseLock(): void;
}

Hierarchy (View Summary)

Properties

Methods

Properties

closed: Promise<undefined>

Methods

  • Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamBYOBRequest.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamBYOBRequest.html new file mode 100644 index 000000000..a0085f9a8 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamBYOBRequest.html @@ -0,0 +1,8 @@ +ReadableStreamBYOBRequest | @wailsio/runtime

Interface ReadableStreamBYOBRequest

interface ReadableStreamBYOBRequest {
    view: null | ArrayBufferView<ArrayBufferLike>;
    respond(bytesWritten: number): void;
    respondWithNewView(view: ArrayBufferView): void;
}

Properties

Methods

Properties

Methods

  • Parameters

    • bytesWritten: number

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamDefaultController.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamDefaultController.html new file mode 100644 index 000000000..0dc9a2850 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamDefaultController.html @@ -0,0 +1,10 @@ +ReadableStreamDefaultController | @wailsio/runtime

Interface ReadableStreamDefaultController<R>

interface ReadableStreamDefaultController<R = any> {
    desiredSize: null | number;
    close(): void;
    enqueue(chunk?: R): void;
    error(e?: any): void;
}

Type Parameters

  • R = any

Properties

Methods

Properties

desiredSize: null | number

Methods

  • Returns void

  • Parameters

    • Optionalchunk: R

    Returns void

  • Parameters

    • Optionale: any

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamDefaultReader.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamDefaultReader.html new file mode 100644 index 000000000..1a8f3bc8d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamDefaultReader.html @@ -0,0 +1,10 @@ +ReadableStreamDefaultReader | @wailsio/runtime

Interface ReadableStreamDefaultReader<R>

interface ReadableStreamDefaultReader<R = any> {
    closed: Promise<undefined>;
    cancel(reason?: any): Promise<void>;
    read(): Promise<ReadableStreamReadResult<R>>;
    releaseLock(): void;
}

Type Parameters

  • R = any

Hierarchy (View Summary)

Properties

Methods

Properties

closed: Promise<undefined>

Methods

  • Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamGenericReader.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamGenericReader.html new file mode 100644 index 000000000..fc5eca6a5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamGenericReader.html @@ -0,0 +1,5 @@ +ReadableStreamGenericReader | @wailsio/runtime

Interface ReadableStreamGenericReader

interface ReadableStreamGenericReader {
    closed: Promise<undefined>;
    cancel(reason?: any): Promise<void>;
}

Hierarchy (View Summary)

Properties

Methods

Properties

closed: Promise<undefined>

Methods

  • Parameters

    • Optionalreason: any

    Returns Promise<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamGetReaderOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamGetReaderOptions.html new file mode 100644 index 000000000..f71b8b580 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamGetReaderOptions.html @@ -0,0 +1,4 @@ +ReadableStreamGetReaderOptions | @wailsio/runtime

Interface ReadableStreamGetReaderOptions

interface ReadableStreamGetReaderOptions {
    mode?: "byob";
}

Properties

Properties

mode?: "byob"

Creates a ReadableStreamBYOBReader and locks the stream to the new reader.

+

This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamReadDoneResult.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamReadDoneResult.html new file mode 100644 index 000000000..d844ef013 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamReadDoneResult.html @@ -0,0 +1,3 @@ +ReadableStreamReadDoneResult | @wailsio/runtime

Interface ReadableStreamReadDoneResult<T>

interface ReadableStreamReadDoneResult<T> {
    done: true;
    value?: T;
}

Type Parameters

  • T

Properties

Properties

done: true
value?: T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamReadValueResult.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamReadValueResult.html new file mode 100644 index 000000000..a9b44ff4e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableStreamReadValueResult.html @@ -0,0 +1,3 @@ +ReadableStreamReadValueResult | @wailsio/runtime

Interface ReadableStreamReadValueResult<T>

interface ReadableStreamReadValueResult<T> {
    done: false;
    value: T;
}

Type Parameters

  • T

Properties

Properties

done: false
value: T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableWritablePair.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableWritablePair.html new file mode 100644 index 000000000..33668f35b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.ReadableWritablePair.html @@ -0,0 +1,5 @@ +ReadableWritablePair | @wailsio/runtime

Interface ReadableWritablePair<R, W>

interface ReadableWritablePair<R = any, W = any> {
    readable: ReadableStream<R>;
    writable: WritableStream<W>;
}

Type Parameters

  • R = any
  • W = any

Properties

Properties

readable: ReadableStream<R>
writable: WritableStream<W>

Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use.

+

Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Size.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Size.html new file mode 100644 index 000000000..6281de1f9 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.Size.html @@ -0,0 +1,6 @@ +Size | @wailsio/runtime

A record describing the size of a window.

+
interface Size {
    height: number;
    width: number;
}

Properties

Properties

height: number

The height of the window.

+
width: number

The width of the window.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.SourceBuffer.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.SourceBuffer.html new file mode 100644 index 000000000..801525c19 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.SourceBuffer.html @@ -0,0 +1,53 @@ +SourceBuffer | @wailsio/runtime

A chunk of media to be passed into an HTMLMediaElement and played, via a MediaSource object. This can be made up of one or several media segments.

+

MDN Reference

+
interface SourceBuffer {
    appendWindowEnd: number;
    appendWindowStart: number;
    buffered: TimeRanges;
    mode: AppendMode;
    onabort: null | (this: SourceBuffer, ev: Event) => any;
    onerror: null | (this: SourceBuffer, ev: Event) => any;
    onupdate: null | (this: SourceBuffer, ev: Event) => any;
    onupdateend: null | (this: SourceBuffer, ev: Event) => any;
    onupdatestart: null | (this: SourceBuffer, ev: Event) => any;
    timestampOffset: number;
    updating: boolean;
    abort(): void;
    addEventListener<K extends keyof SourceBufferEventMap>(
        type: K,
        listener: (this: SourceBuffer, ev: SourceBufferEventMap[K]) => any,
        options?: boolean | AddEventListenerOptions,
    ): void;
    addEventListener(
        type: string,
        listener: EventListenerOrEventListenerObject,
        options?: boolean | AddEventListenerOptions,
    ): void;
    appendBuffer(data: BufferSource): void;
    changeType(type: string): void;
    dispatchEvent(event: Event): boolean;
    remove(start: number, end: number): void;
    removeEventListener<K extends keyof SourceBufferEventMap>(
        type: K,
        listener: (this: SourceBuffer, ev: SourceBufferEventMap[K]) => any,
        options?: boolean | EventListenerOptions,
    ): void;
    removeEventListener(
        type: string,
        listener: EventListenerOrEventListenerObject,
        options?: boolean | EventListenerOptions,
    ): void;
}

Hierarchy (View Summary)

Properties

appendWindowEnd: number
appendWindowStart: number
buffered: TimeRanges
onabort: null | (this: SourceBuffer, ev: Event) => any
onerror: null | (this: SourceBuffer, ev: Event) => any
onupdate: null | (this: SourceBuffer, ev: Event) => any
onupdateend: null | (this: SourceBuffer, ev: Event) => any
onupdatestart: null | (this: SourceBuffer, ev: Event) => any
timestampOffset: number
updating: boolean

Methods

  • Returns void

  • Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

    +

    The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture.

    +

    When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET.

    +

    When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

    +

    When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed.

    +

    If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted.

    +

    The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture.

    +

    MDN Reference

    +

    Type Parameters

    Parameters

    Returns void

  • Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

    +

    The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture.

    +

    When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET.

    +

    When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

    +

    When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed.

    +

    If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted.

    +

    The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture.

    +

    MDN Reference

    +

    Parameters

    Returns void

  • Parameters

    • type: string

    Returns void

  • Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.

    +

    MDN Reference

    +

    Parameters

    Returns boolean

  • Parameters

    • start: number
    • end: number

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.SourceBufferEventMap.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.SourceBufferEventMap.html new file mode 100644 index 000000000..95e9fee8d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.SourceBufferEventMap.html @@ -0,0 +1,6 @@ +SourceBufferEventMap | @wailsio/runtime
interface SourceBufferEventMap {
    abort: Event;
    error: Event;
    update: Event;
    updateend: Event;
    updatestart: Event;
}

Properties

abort: Event
error: Event
update: Event
updateend: Event
updatestart: Event
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.StreamPipeOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.StreamPipeOptions.html new file mode 100644 index 000000000..ec92b448b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.StreamPipeOptions.html @@ -0,0 +1,13 @@ +StreamPipeOptions | @wailsio/runtime
interface StreamPipeOptions {
    preventAbort?: boolean;
    preventCancel?: boolean;
    preventClose?: boolean;
    signal?: AbortSignal;
}

Properties

preventAbort?: boolean
preventCancel?: boolean
preventClose?: boolean

Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered.

+

Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.

+

Errors and closures of the source and destination streams propagate as follows:

+

An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination.

+

An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source.

+

When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error.

+

If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source.

+

The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set.

+
signal?: AbortSignal
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.TimeRanges.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.TimeRanges.html new file mode 100644 index 000000000..f93dd4e77 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.TimeRanges.html @@ -0,0 +1,14 @@ +TimeRanges | @wailsio/runtime

Used to represent a set of time ranges, primarily for the purpose of tracking which portions of media have been buffered when loading it for use by the

+

MDN Reference

+
interface TimeRanges {
    length: number;
    end(index: number): number;
    start(index: number): number;
}

Properties

Methods

Properties

length: number

Returns the number of ranges in the object.

+

MDN Reference

+

Methods

  • Returns the time for the end of the range with the given index.

    +

    Throws an "IndexSizeError" DOMException if the index is out of range.

    +

    MDN Reference

    +

    Parameters

    • index: number

    Returns number

  • Returns the time for the start of the range with the given index.

    +

    Throws an "IndexSizeError" DOMException if the index is out of range.

    +

    MDN Reference

    +

    Parameters

    • index: number

    Returns number

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.URL.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.URL.html new file mode 100644 index 000000000..171f46a55 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.URL.html @@ -0,0 +1,30 @@ +URL | @wailsio/runtime

The URL interface represents an object providing static methods used for creating object URLs.

+

MDN Reference

+
interface URL {
    hash: string;
    host: string;
    hostname: string;
    href: string;
    origin: string;
    password: string;
    pathname: string;
    port: string;
    protocol: string;
    search: string;
    searchParams: URLSearchParams;
    username: string;
    toJSON(): string;
    toString(): string;
}

Properties

hash: string
host: string
hostname: string
href: string
origin: string
password: string
pathname: string
port: string
protocol: string
search: string
searchParams: URLSearchParams
username: string

Methods

  • Returns string

  • Returns string

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingByteSource.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingByteSource.html new file mode 100644 index 000000000..f4d07fbf6 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingByteSource.html @@ -0,0 +1,6 @@ +UnderlyingByteSource | @wailsio/runtime
interface UnderlyingByteSource {
    autoAllocateChunkSize?: number;
    cancel?: UnderlyingSourceCancelCallback;
    pull?: (
        controller: ReadableByteStreamController,
    ) => void | PromiseLike<void>;
    start?: (controller: ReadableByteStreamController) => any;
    type: "bytes";
}

Properties

autoAllocateChunkSize?: number
pull?: (controller: ReadableByteStreamController) => void | PromiseLike<void>
start?: (controller: ReadableByteStreamController) => any
type: "bytes"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingDefaultSource.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingDefaultSource.html new file mode 100644 index 000000000..d282894c4 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingDefaultSource.html @@ -0,0 +1,5 @@ +UnderlyingDefaultSource | @wailsio/runtime

Interface UnderlyingDefaultSource<R>

interface UnderlyingDefaultSource<R = any> {
    cancel?: UnderlyingSourceCancelCallback;
    pull?: (
        controller: ReadableStreamDefaultController<R>,
    ) => void | PromiseLike<void>;
    start?: (controller: ReadableStreamDefaultController<R>) => any;
    type?: undefined;
}

Type Parameters

  • R = any

Properties

Properties

pull?: (
    controller: ReadableStreamDefaultController<R>,
) => void | PromiseLike<void>
start?: (controller: ReadableStreamDefaultController<R>) => any
type?: undefined
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSink.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSink.html new file mode 100644 index 000000000..d6da3c8d4 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSink.html @@ -0,0 +1,6 @@ +UnderlyingSink | @wailsio/runtime

Interface UnderlyingSink<W>

interface UnderlyingSink<W = any> {
    abort?: UnderlyingSinkAbortCallback;
    close?: UnderlyingSinkCloseCallback;
    start?: UnderlyingSinkStartCallback;
    type?: undefined;
    write?: UnderlyingSinkWriteCallback<W>;
}

Type Parameters

  • W = any

Properties

Properties

type?: undefined
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkAbortCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkAbortCallback.html new file mode 100644 index 000000000..e45d9d445 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkAbortCallback.html @@ -0,0 +1 @@ +UnderlyingSinkAbortCallback | @wailsio/runtime

Interface UnderlyingSinkAbortCallback

  • Parameters

    • Optionalreason: any

    Returns void | PromiseLike<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkCloseCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkCloseCallback.html new file mode 100644 index 000000000..06c0a3608 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkCloseCallback.html @@ -0,0 +1 @@ +UnderlyingSinkCloseCallback | @wailsio/runtime

Interface UnderlyingSinkCloseCallback

  • Returns void | PromiseLike<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkStartCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkStartCallback.html new file mode 100644 index 000000000..b3fa49c5c --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkStartCallback.html @@ -0,0 +1 @@ +UnderlyingSinkStartCallback | @wailsio/runtime

Interface UnderlyingSinkStartCallback

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkWriteCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkWriteCallback.html new file mode 100644 index 000000000..81ad323e5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSinkWriteCallback.html @@ -0,0 +1 @@ +UnderlyingSinkWriteCallback | @wailsio/runtime

Interface UnderlyingSinkWriteCallback<W>

Type Parameters

  • W
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSource.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSource.html new file mode 100644 index 000000000..c95e2903c --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSource.html @@ -0,0 +1,6 @@ +UnderlyingSource | @wailsio/runtime

Interface UnderlyingSource<R>

interface UnderlyingSource<R = any> {
    autoAllocateChunkSize?: number;
    cancel?: UnderlyingSourceCancelCallback;
    pull?: UnderlyingSourcePullCallback<R>;
    start?: UnderlyingSourceStartCallback<R>;
    type?: "bytes";
}

Type Parameters

  • R = any

Properties

autoAllocateChunkSize?: number
type?: "bytes"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourceCancelCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourceCancelCallback.html new file mode 100644 index 000000000..978b039e9 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourceCancelCallback.html @@ -0,0 +1 @@ +UnderlyingSourceCancelCallback | @wailsio/runtime

Interface UnderlyingSourceCancelCallback

  • Parameters

    • Optionalreason: any

    Returns void | PromiseLike<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourcePullCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourcePullCallback.html new file mode 100644 index 000000000..8779e3a64 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourcePullCallback.html @@ -0,0 +1 @@ +UnderlyingSourcePullCallback | @wailsio/runtime

Interface UnderlyingSourcePullCallback<R>

Type Parameters

  • R
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourceStartCallback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourceStartCallback.html new file mode 100644 index 000000000..acfdef628 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.UnderlyingSourceStartCallback.html @@ -0,0 +1 @@ +UnderlyingSourceStartCallback | @wailsio/runtime

Interface UnderlyingSourceStartCallback<R>

Type Parameters

  • R
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStream.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStream.html new file mode 100644 index 000000000..fcaeff279 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStream.html @@ -0,0 +1,11 @@ +WritableStream | @wailsio/runtime

Interface WritableStream<W>

This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing.

+

MDN Reference

+
interface WritableStream<W = any> {
    locked: boolean;
    abort(reason?: any): Promise<void>;
    close(): Promise<void>;
    getWriter(): WritableStreamDefaultWriter<W>;
}

Type Parameters

  • W = any

Properties

Methods

Properties

locked: boolean

Methods

  • Parameters

    • Optionalreason: any

    Returns Promise<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStreamDefaultController.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStreamDefaultController.html new file mode 100644 index 000000000..c51cc6c16 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStreamDefaultController.html @@ -0,0 +1,7 @@ +WritableStreamDefaultController | @wailsio/runtime

Interface WritableStreamDefaultController

This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate.

+

MDN Reference

+
interface WritableStreamDefaultController {
    signal: AbortSignal;
    error(e?: any): void;
}

Properties

Methods

Properties

signal: AbortSignal

Methods

  • Parameters

    • Optionale: any

    Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStreamDefaultWriter.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStreamDefaultWriter.html new file mode 100644 index 000000000..d0d4f5ed7 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/interfaces/_internal_.WritableStreamDefaultWriter.html @@ -0,0 +1,17 @@ +WritableStreamDefaultWriter | @wailsio/runtime

Interface WritableStreamDefaultWriter<W>

This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink.

+

MDN Reference

+
interface WritableStreamDefaultWriter<W = any> {
    closed: Promise<undefined>;
    desiredSize: null | number;
    ready: Promise<undefined>;
    abort(reason?: any): Promise<void>;
    close(): Promise<void>;
    releaseLock(): void;
    write(chunk?: W): Promise<void>;
}

Type Parameters

  • W = any

Properties

closed: Promise<undefined>
desiredSize: null | number
ready: Promise<undefined>

Methods

  • Parameters

    • Optionalreason: any

    Returns Promise<void>

  • Returns void

  • Parameters

    • Optionalchunk: W

    Returns Promise<void>

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules.html new file mode 100644 index 000000000..4248cf28d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules.html @@ -0,0 +1 @@ +@wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Application.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Application.html new file mode 100644 index 000000000..e6b5d57c5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Application.html @@ -0,0 +1 @@ +Application | @wailsio/runtime

Namespace Application

Functions

Hide
Quit
Show
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Browser.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Browser.html new file mode 100644 index 000000000..757bf8469 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Browser.html @@ -0,0 +1 @@ +Browser | @wailsio/runtime

Namespace Browser

Functions

OpenURL
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Call.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Call.html new file mode 100644 index 000000000..8d06195ea --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Call.html @@ -0,0 +1 @@ +Call | @wailsio/runtime

Namespace Call

Classes

RuntimeError

Type Aliases

CallOptions

Functions

ByID
ByName
Call
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Clipboard.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Clipboard.html new file mode 100644 index 000000000..1d15bfa14 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Clipboard.html @@ -0,0 +1 @@ +Clipboard | @wailsio/runtime

Namespace Clipboard

Functions

SetText
Text
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Dialogs.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Dialogs.html new file mode 100644 index 000000000..256f30a47 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Dialogs.html @@ -0,0 +1 @@ +Dialogs | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Events.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Events.html new file mode 100644 index 000000000..cd12fa65d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Events.html @@ -0,0 +1 @@ +Events | @wailsio/runtime

Namespace Events

Classes

WailsEvent

Type Aliases

Callback

Variables

Types

Functions

Emit
Off
OffAll
On
Once
OnMultiple
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Flags.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Flags.html new file mode 100644 index 000000000..2004c8692 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Flags.html @@ -0,0 +1 @@ +Flags | @wailsio/runtime

Namespace Flags

Functions

GetFlag
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Screens.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Screens.html new file mode 100644 index 000000000..f25eaabb3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/Screens.html @@ -0,0 +1 @@ +Screens | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/System.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/System.html new file mode 100644 index 000000000..c9043eddd --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/System.html @@ -0,0 +1 @@ +System | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/WML.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/WML.html new file mode 100644 index 000000000..e2d7d3912 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/WML.html @@ -0,0 +1 @@ +WML | @wailsio/runtime

Namespace WML

Functions

Enable
Reload
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/_internal_.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/_internal_.html new file mode 100644 index 000000000..9707760fd --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/modules/_internal_.html @@ -0,0 +1 @@ +<internal> | @wailsio/runtime

Classes

Window

Interfaces

AddEventListenerOptions
ArrayBufferView
Blob
BlobPropertyBag
ErrorOptions
Event
EventInit
EventListener
EventListenerObject
EventListenerOptions
EventTarget
Iterable
MediaSource
MediaSourceEventMap
Position
PromiseFulfilledResult
PromiseLike
PromiseRejectedResult
PromiseWithResolvers
QueuingStrategy
QueuingStrategySize
ReadableByteStreamController
ReadableStream
ReadableStreamBYOBReader
ReadableStreamBYOBRequest
ReadableStreamDefaultController
ReadableStreamDefaultReader
ReadableStreamGenericReader
ReadableStreamGetReaderOptions
ReadableStreamReadDoneResult
ReadableStreamReadValueResult
ReadableWritablePair
Size
SourceBuffer
SourceBufferEventMap
StreamPipeOptions
TimeRanges
UnderlyingByteSource
UnderlyingDefaultSource
UnderlyingSink
UnderlyingSinkAbortCallback
UnderlyingSinkCloseCallback
UnderlyingSinkStartCallback
UnderlyingSinkWriteCallback
UnderlyingSource
UnderlyingSourceCancelCallback
UnderlyingSourcePullCallback
UnderlyingSourceStartCallback
URL
WritableStream
WritableStreamDefaultController
WritableStreamDefaultWriter

Type Aliases

AppendMode
ArrayBufferLike
Awaited
BlobPart
BufferSource
CancellablePromiseCanceller
CancellablePromiseExecutor
CancellablePromiseRejector
CancellablePromiseResolver
EndingType
EndOfStreamError
EventListenerOrEventListenerObject
Partial
PromiseSettledResult
ReadableStreamController
ReadableStreamReader
ReadableStreamReadResult
Readonly
ReadyState
Record

Variables

Blob
Event
EventTarget
MediaSource
ReadableByteStreamController
ReadableStream
ReadableStreamBYOBReader
ReadableStreamBYOBRequest
ReadableStreamDefaultController
ReadableStreamDefaultReader
SourceBuffer
TimeRanges
URL
WritableStream
WritableStreamDefaultController
WritableStreamDefaultWriter
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/Call.CallOptions.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/Call.CallOptions.html new file mode 100644 index 000000000..8bb2ba3c9 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/Call.CallOptions.html @@ -0,0 +1,9 @@ +CallOptions | @wailsio/runtime

Type Alias CallOptions

CallOptions:
    | { args: any[]; methodID: number; methodName?: never }
    | { args: any[]; methodID?: never; methodName: string }

Holds all required information for a binding call. +May provide either a method ID or a method name, but not both.

+

Type declaration

  • { args: any[]; methodID: number; methodName?: never }
    • args: any[]

      Arguments to be passed into the bound method.

      +
    • methodID: number

      The numeric ID of the bound method to call.

      +
    • OptionalmethodName?: never

      The fully qualified name of the bound method to call.

      +
  • { args: any[]; methodID?: never; methodName: string }
    • args: any[]

      Arguments to be passed into the bound method.

      +
    • OptionalmethodID?: never

      The numeric ID of the bound method to call.

      +
    • methodName: string

      The fully qualified name of the bound method to call.

      +
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/Events.Callback.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/Events.Callback.html new file mode 100644 index 000000000..03c56fff2 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/Events.Callback.html @@ -0,0 +1,2 @@ +Callback | @wailsio/runtime
Callback: (ev: WailsEvent) => void

The type of handlers for a given event.

+

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.AppendMode.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.AppendMode.html new file mode 100644 index 000000000..21279131a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.AppendMode.html @@ -0,0 +1 @@ +AppendMode | @wailsio/runtime
AppendMode: "segments" | "sequence"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ArrayBufferLike.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ArrayBufferLike.html new file mode 100644 index 000000000..27533a820 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ArrayBufferLike.html @@ -0,0 +1 @@ +ArrayBufferLike | @wailsio/runtime
ArrayBufferLike: ArrayBufferTypes[keyof ArrayBufferTypes]
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Awaited.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Awaited.html new file mode 100644 index 000000000..7d51c3785 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Awaited.html @@ -0,0 +1,2 @@ +Awaited | @wailsio/runtime
Awaited: T extends null
| undefined
    ? T
    : T extends object & { then(onfulfilled: F, ...args: _): any }
        ? F extends (value: infer V, ...args: infer _) => any
            ? Awaited<V>
            : never
        : T

Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to never. This emulates the behavior of await.

+

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.BlobPart.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.BlobPart.html new file mode 100644 index 000000000..116be5557 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.BlobPart.html @@ -0,0 +1 @@ +BlobPart | @wailsio/runtime
BlobPart: BufferSource | Blob | string
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.BufferSource.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.BufferSource.html new file mode 100644 index 000000000..c4ad275f4 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.BufferSource.html @@ -0,0 +1 @@ +BufferSource | @wailsio/runtime
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseCanceller.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseCanceller.html new file mode 100644 index 000000000..3258a70d0 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseCanceller.html @@ -0,0 +1 @@ +CancellablePromiseCanceller | @wailsio/runtime

Type Alias CancellablePromiseCanceller

CancellablePromiseCanceller: (cause?: any) => void | PromiseLike<void>

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseExecutor.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseExecutor.html new file mode 100644 index 000000000..85b0f8526 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseExecutor.html @@ -0,0 +1 @@ +CancellablePromiseExecutor | @wailsio/runtime

Type Alias CancellablePromiseExecutor<T>

CancellablePromiseExecutor: (
    resolve: CancellablePromiseResolver<T>,
    reject: CancellablePromiseRejector,
) => void

Type Parameters

  • T

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseRejector.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseRejector.html new file mode 100644 index 000000000..b75c628c3 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseRejector.html @@ -0,0 +1 @@ +CancellablePromiseRejector | @wailsio/runtime

Type Alias CancellablePromiseRejector

CancellablePromiseRejector: (reason?: any) => void

Type declaration

    • (reason?: any): void
    • Parameters

      • Optionalreason: any

      Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseResolver.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseResolver.html new file mode 100644 index 000000000..81f2b806b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.CancellablePromiseResolver.html @@ -0,0 +1 @@ +CancellablePromiseResolver | @wailsio/runtime

Type Alias CancellablePromiseResolver<T>

CancellablePromiseResolver: (
    value: T | PromiseLike<T> | CancellablePromiseLike<T>,
) => void

Type Parameters

  • T

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EndOfStreamError.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EndOfStreamError.html new file mode 100644 index 000000000..df2e1e6b6 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EndOfStreamError.html @@ -0,0 +1 @@ +EndOfStreamError | @wailsio/runtime
EndOfStreamError: "decode" | "network"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EndingType.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EndingType.html new file mode 100644 index 000000000..97b2a0bfe --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EndingType.html @@ -0,0 +1 @@ +EndingType | @wailsio/runtime
EndingType: "native" | "transparent"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EventListenerOrEventListenerObject.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EventListenerOrEventListenerObject.html new file mode 100644 index 000000000..c0e2bfa6d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.EventListenerOrEventListenerObject.html @@ -0,0 +1 @@ +EventListenerOrEventListenerObject | @wailsio/runtime

Type Alias EventListenerOrEventListenerObject

EventListenerOrEventListenerObject: EventListener | EventListenerObject
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Partial.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Partial.html new file mode 100644 index 000000000..c2c89e799 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Partial.html @@ -0,0 +1,2 @@ +Partial | @wailsio/runtime
Partial: { [P in keyof T]?: T[P] }

Make all properties in T optional

+

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.PromiseSettledResult.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.PromiseSettledResult.html new file mode 100644 index 000000000..c71a7894b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.PromiseSettledResult.html @@ -0,0 +1 @@ +PromiseSettledResult | @wailsio/runtime

Type Alias PromiseSettledResult<T>

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamController.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamController.html new file mode 100644 index 000000000..03ff250f6 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamController.html @@ -0,0 +1 @@ +ReadableStreamController | @wailsio/runtime

Type Alias ReadableStreamController<T>

ReadableStreamController:
    | ReadableStreamDefaultController<T>
    | ReadableByteStreamController

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamReadResult.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamReadResult.html new file mode 100644 index 000000000..300a4a370 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamReadResult.html @@ -0,0 +1 @@ +ReadableStreamReadResult | @wailsio/runtime

Type Alias ReadableStreamReadResult<T>

ReadableStreamReadResult:
    | ReadableStreamReadValueResult<T>
    | ReadableStreamReadDoneResult<T>

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamReader.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamReader.html new file mode 100644 index 000000000..a6eb2921a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadableStreamReader.html @@ -0,0 +1 @@ +ReadableStreamReader | @wailsio/runtime

Type Alias ReadableStreamReader<T>

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Readonly.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Readonly.html new file mode 100644 index 000000000..3f66bd1ba --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Readonly.html @@ -0,0 +1,2 @@ +Readonly | @wailsio/runtime
Readonly: { readonly [P in keyof T]: T[P] }

Make all properties in T readonly

+

Type Parameters

  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadyState.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadyState.html new file mode 100644 index 000000000..cab35e873 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.ReadyState.html @@ -0,0 +1 @@ +ReadyState | @wailsio/runtime
ReadyState: "closed" | "ended" | "open"
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Record.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Record.html new file mode 100644 index 000000000..8930f14b9 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/types/_internal_.Record.html @@ -0,0 +1,2 @@ +Record | @wailsio/runtime

Type Alias Record<K, T>

Record: { [P in K]: T }

Construct a type with a set of properties K of type T

+

Type Parameters

  • K extends keyof any
  • T
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/Events.Types.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/Events.Types.html new file mode 100644 index 000000000..6ca5a3550 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/Events.Types.html @@ -0,0 +1 @@ +Types | @wailsio/runtime

Variable TypesConst

Types: Readonly<
    {
        Common: Readonly<
            {
                ApplicationLaunchedWithUrl: "common:ApplicationLaunchedWithUrl";
                ApplicationOpenedWithFile: "common:ApplicationOpenedWithFile";
                ApplicationStarted: "common:ApplicationStarted";
                ThemeChanged: "common:ThemeChanged";
                WindowClosing: "common:WindowClosing";
                WindowDidMove: "common:WindowDidMove";
                WindowDidResize: "common:WindowDidResize";
                WindowDPIChanged: "common:WindowDPIChanged";
                WindowDropZoneFilesDropped: "common:WindowDropZoneFilesDropped";
                WindowFilesDropped: "common:WindowFilesDropped";
                WindowFocus: "common:WindowFocus";
                WindowFullscreen: "common:WindowFullscreen";
                WindowHide: "common:WindowHide";
                WindowLostFocus: "common:WindowLostFocus";
                WindowMaximise: "common:WindowMaximise";
                WindowMinimise: "common:WindowMinimise";
                WindowRestore: "common:WindowRestore";
                WindowRuntimeReady: "common:WindowRuntimeReady";
                WindowShow: "common:WindowShow";
                WindowToggleFrameless: "common:WindowToggleFrameless";
                WindowUnFullscreen: "common:WindowUnFullscreen";
                WindowUnMaximise: "common:WindowUnMaximise";
                WindowUnMinimise: "common:WindowUnMinimise";
                WindowZoom: "common:WindowZoom";
                WindowZoomIn: "common:WindowZoomIn";
                WindowZoomOut: "common:WindowZoomOut";
                WindowZoomReset: "common:WindowZoomReset";
            },
        >;
        Linux: Readonly<
            {
                ApplicationStartup: "linux:ApplicationStartup";
                SystemThemeChanged: "linux:SystemThemeChanged";
                WindowDeleteEvent: "linux:WindowDeleteEvent";
                WindowDidMove: "linux:WindowDidMove";
                WindowDidResize: "linux:WindowDidResize";
                WindowFocusIn: "linux:WindowFocusIn";
                WindowFocusOut: "linux:WindowFocusOut";
                WindowLoadChanged: "linux:WindowLoadChanged";
            },
        >;
        Mac: Readonly<
            {
                ApplicationDidBecomeActive: "mac:ApplicationDidBecomeActive";
                ApplicationDidChangeBackingProperties: "mac:ApplicationDidChangeBackingProperties";
                ApplicationDidChangeEffectiveAppearance: "mac:ApplicationDidChangeEffectiveAppearance";
                ApplicationDidChangeIcon: "mac:ApplicationDidChangeIcon";
                ApplicationDidChangeOcclusionState: "mac:ApplicationDidChangeOcclusionState";
                ApplicationDidChangeScreenParameters: "mac:ApplicationDidChangeScreenParameters";
                ApplicationDidChangeStatusBarFrame: "mac:ApplicationDidChangeStatusBarFrame";
                ApplicationDidChangeStatusBarOrientation: "mac:ApplicationDidChangeStatusBarOrientation";
                ApplicationDidChangeTheme: "mac:ApplicationDidChangeTheme";
                ApplicationDidFinishLaunching: "mac:ApplicationDidFinishLaunching";
                ApplicationDidHide: "mac:ApplicationDidHide";
                ApplicationDidResignActive: "mac:ApplicationDidResignActive";
                ApplicationDidUnhide: "mac:ApplicationDidUnhide";
                ApplicationDidUpdate: "mac:ApplicationDidUpdate";
                ApplicationShouldHandleReopen: "mac:ApplicationShouldHandleReopen";
                ApplicationWillBecomeActive: "mac:ApplicationWillBecomeActive";
                ApplicationWillFinishLaunching: "mac:ApplicationWillFinishLaunching";
                ApplicationWillHide: "mac:ApplicationWillHide";
                ApplicationWillResignActive: "mac:ApplicationWillResignActive";
                ApplicationWillTerminate: "mac:ApplicationWillTerminate";
                ApplicationWillUnhide: "mac:ApplicationWillUnhide";
                ApplicationWillUpdate: "mac:ApplicationWillUpdate";
                MenuDidAddItem: "mac:MenuDidAddItem";
                MenuDidBeginTracking: "mac:MenuDidBeginTracking";
                MenuDidClose: "mac:MenuDidClose";
                MenuDidDisplayItem: "mac:MenuDidDisplayItem";
                MenuDidEndTracking: "mac:MenuDidEndTracking";
                MenuDidHighlightItem: "mac:MenuDidHighlightItem";
                MenuDidOpen: "mac:MenuDidOpen";
                MenuDidPopUp: "mac:MenuDidPopUp";
                MenuDidRemoveItem: "mac:MenuDidRemoveItem";
                MenuDidSendAction: "mac:MenuDidSendAction";
                MenuDidSendActionToItem: "mac:MenuDidSendActionToItem";
                MenuDidUpdate: "mac:MenuDidUpdate";
                MenuWillAddItem: "mac:MenuWillAddItem";
                MenuWillBeginTracking: "mac:MenuWillBeginTracking";
                MenuWillDisplayItem: "mac:MenuWillDisplayItem";
                MenuWillEndTracking: "mac:MenuWillEndTracking";
                MenuWillHighlightItem: "mac:MenuWillHighlightItem";
                MenuWillOpen: "mac:MenuWillOpen";
                MenuWillPopUp: "mac:MenuWillPopUp";
                MenuWillRemoveItem: "mac:MenuWillRemoveItem";
                MenuWillSendAction: "mac:MenuWillSendAction";
                MenuWillSendActionToItem: "mac:MenuWillSendActionToItem";
                MenuWillUpdate: "mac:MenuWillUpdate";
                WebViewDidCommitNavigation: "mac:WebViewDidCommitNavigation";
                WebViewDidFinishNavigation: "mac:WebViewDidFinishNavigation";
                WebViewDidReceiveServerRedirectForProvisionalNavigation: "mac:WebViewDidReceiveServerRedirectForProvisionalNavigation";
                WebViewDidStartProvisionalNavigation: "mac:WebViewDidStartProvisionalNavigation";
                WindowDidBecomeKey: "mac:WindowDidBecomeKey";
                WindowDidBecomeMain: "mac:WindowDidBecomeMain";
                WindowDidBeginSheet: "mac:WindowDidBeginSheet";
                WindowDidChangeAlpha: "mac:WindowDidChangeAlpha";
                WindowDidChangeBackingLocation: "mac:WindowDidChangeBackingLocation";
                WindowDidChangeBackingProperties: "mac:WindowDidChangeBackingProperties";
                WindowDidChangeCollectionBehavior: "mac:WindowDidChangeCollectionBehavior";
                WindowDidChangeEffectiveAppearance: "mac:WindowDidChangeEffectiveAppearance";
                WindowDidChangeOcclusionState: "mac:WindowDidChangeOcclusionState";
                WindowDidChangeOrderingMode: "mac:WindowDidChangeOrderingMode";
                WindowDidChangeScreen: "mac:WindowDidChangeScreen";
                WindowDidChangeScreenParameters: "mac:WindowDidChangeScreenParameters";
                WindowDidChangeScreenProfile: "mac:WindowDidChangeScreenProfile";
                WindowDidChangeScreenSpace: "mac:WindowDidChangeScreenSpace";
                WindowDidChangeScreenSpaceProperties: "mac:WindowDidChangeScreenSpaceProperties";
                WindowDidChangeSharingType: "mac:WindowDidChangeSharingType";
                WindowDidChangeSpace: "mac:WindowDidChangeSpace";
                WindowDidChangeSpaceOrderingMode: "mac:WindowDidChangeSpaceOrderingMode";
                WindowDidChangeTitle: "mac:WindowDidChangeTitle";
                WindowDidChangeToolbar: "mac:WindowDidChangeToolbar";
                WindowDidDeminiaturize: "mac:WindowDidDeminiaturize";
                WindowDidEndSheet: "mac:WindowDidEndSheet";
                WindowDidEnterFullScreen: "mac:WindowDidEnterFullScreen";
                WindowDidEnterVersionBrowser: "mac:WindowDidEnterVersionBrowser";
                WindowDidExitFullScreen: "mac:WindowDidExitFullScreen";
                WindowDidExitVersionBrowser: "mac:WindowDidExitVersionBrowser";
                WindowDidExpose: "mac:WindowDidExpose";
                WindowDidFocus: "mac:WindowDidFocus";
                WindowDidMiniaturize: "mac:WindowDidMiniaturize";
                WindowDidMove: "mac:WindowDidMove";
                WindowDidOrderOffScreen: "mac:WindowDidOrderOffScreen";
                WindowDidOrderOnScreen: "mac:WindowDidOrderOnScreen";
                WindowDidResignKey: "mac:WindowDidResignKey";
                WindowDidResignMain: "mac:WindowDidResignMain";
                WindowDidResize: "mac:WindowDidResize";
                WindowDidUpdate: "mac:WindowDidUpdate";
                WindowDidUpdateAlpha: "mac:WindowDidUpdateAlpha";
                WindowDidUpdateCollectionBehavior: "mac:WindowDidUpdateCollectionBehavior";
                WindowDidUpdateCollectionProperties: "mac:WindowDidUpdateCollectionProperties";
                WindowDidUpdateShadow: "mac:WindowDidUpdateShadow";
                WindowDidUpdateTitle: "mac:WindowDidUpdateTitle";
                WindowDidUpdateToolbar: "mac:WindowDidUpdateToolbar";
                WindowDidZoom: "mac:WindowDidZoom";
                WindowFileDraggingEntered: "mac:WindowFileDraggingEntered";
                WindowFileDraggingExited: "mac:WindowFileDraggingExited";
                WindowFileDraggingPerformed: "mac:WindowFileDraggingPerformed";
                WindowHide: "mac:WindowHide";
                WindowMaximise: "mac:WindowMaximise";
                WindowMinimise: "mac:WindowMinimise";
                WindowShouldClose: "mac:WindowShouldClose";
                WindowShow: "mac:WindowShow";
                WindowUnMaximise: "mac:WindowUnMaximise";
                WindowUnMinimise: "mac:WindowUnMinimise";
                WindowWillBecomeKey: "mac:WindowWillBecomeKey";
                WindowWillBecomeMain: "mac:WindowWillBecomeMain";
                WindowWillBeginSheet: "mac:WindowWillBeginSheet";
                WindowWillChangeOrderingMode: "mac:WindowWillChangeOrderingMode";
                WindowWillClose: "mac:WindowWillClose";
                WindowWillDeminiaturize: "mac:WindowWillDeminiaturize";
                WindowWillEnterFullScreen: "mac:WindowWillEnterFullScreen";
                WindowWillEnterVersionBrowser: "mac:WindowWillEnterVersionBrowser";
                WindowWillExitFullScreen: "mac:WindowWillExitFullScreen";
                WindowWillExitVersionBrowser: "mac:WindowWillExitVersionBrowser";
                WindowWillFocus: "mac:WindowWillFocus";
                WindowWillMiniaturize: "mac:WindowWillMiniaturize";
                WindowWillMove: "mac:WindowWillMove";
                WindowWillOrderOffScreen: "mac:WindowWillOrderOffScreen";
                WindowWillOrderOnScreen: "mac:WindowWillOrderOnScreen";
                WindowWillResignMain: "mac:WindowWillResignMain";
                WindowWillResize: "mac:WindowWillResize";
                WindowWillUnfocus: "mac:WindowWillUnfocus";
                WindowWillUpdate: "mac:WindowWillUpdate";
                WindowWillUpdateAlpha: "mac:WindowWillUpdateAlpha";
                WindowWillUpdateCollectionBehavior: "mac:WindowWillUpdateCollectionBehavior";
                WindowWillUpdateCollectionProperties: "mac:WindowWillUpdateCollectionProperties";
                WindowWillUpdateShadow: "mac:WindowWillUpdateShadow";
                WindowWillUpdateTitle: "mac:WindowWillUpdateTitle";
                WindowWillUpdateToolbar: "mac:WindowWillUpdateToolbar";
                WindowWillUpdateVisibility: "mac:WindowWillUpdateVisibility";
                WindowWillUseStandardFrame: "mac:WindowWillUseStandardFrame";
                WindowZoomIn: "mac:WindowZoomIn";
                WindowZoomOut: "mac:WindowZoomOut";
                WindowZoomReset: "mac:WindowZoomReset";
            },
        >;
        Windows: Readonly<
            {
                APMPowerSettingChange: "windows:APMPowerSettingChange";
                APMPowerStatusChange: "windows:APMPowerStatusChange";
                APMResumeAutomatic: "windows:APMResumeAutomatic";
                APMResumeSuspend: "windows:APMResumeSuspend";
                APMSuspend: "windows:APMSuspend";
                ApplicationStarted: "windows:ApplicationStarted";
                SystemThemeChanged: "windows:SystemThemeChanged";
                WebViewNavigationCompleted: "windows:WebViewNavigationCompleted";
                WindowActive: "windows:WindowActive";
                WindowBackgroundErase: "windows:WindowBackgroundErase";
                WindowClickActive: "windows:WindowClickActive";
                WindowClosing: "windows:WindowClosing";
                WindowDidMove: "windows:WindowDidMove";
                WindowDidResize: "windows:WindowDidResize";
                WindowDPIChanged: "windows:WindowDPIChanged";
                WindowDragDrop: "windows:WindowDragDrop";
                WindowDragEnter: "windows:WindowDragEnter";
                WindowDragLeave: "windows:WindowDragLeave";
                WindowDragOver: "windows:WindowDragOver";
                WindowEndMove: "windows:WindowEndMove";
                WindowEndResize: "windows:WindowEndResize";
                WindowFullscreen: "windows:WindowFullscreen";
                WindowHide: "windows:WindowHide";
                WindowInactive: "windows:WindowInactive";
                WindowKeyDown: "windows:WindowKeyDown";
                WindowKeyUp: "windows:WindowKeyUp";
                WindowKillFocus: "windows:WindowKillFocus";
                WindowMaximise: "windows:WindowMaximise";
                WindowMinimise: "windows:WindowMinimise";
                WindowNonClientHit: "windows:WindowNonClientHit";
                WindowNonClientMouseDown: "windows:WindowNonClientMouseDown";
                WindowNonClientMouseLeave: "windows:WindowNonClientMouseLeave";
                WindowNonClientMouseMove: "windows:WindowNonClientMouseMove";
                WindowNonClientMouseUp: "windows:WindowNonClientMouseUp";
                WindowPaint: "windows:WindowPaint";
                WindowRestore: "windows:WindowRestore";
                WindowSetFocus: "windows:WindowSetFocus";
                WindowShow: "windows:WindowShow";
                WindowStartMove: "windows:WindowStartMove";
                WindowStartResize: "windows:WindowStartResize";
                WindowUnFullscreen: "windows:WindowUnFullscreen";
                WindowUnMaximise: "windows:WindowUnMaximise";
                WindowUnMinimise: "windows:WindowUnMinimise";
                WindowZOrderChanged: "windows:WindowZOrderChanged";
            },
        >;
    },
> = ...
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/Window.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/Window.html new file mode 100644 index 000000000..ba3122b45 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/Window.html @@ -0,0 +1,2 @@ +Window | @wailsio/runtime

Variable WindowConst

Window: Window = ...

The window within which the script is running.

+
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.Blob-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.Blob-1.html new file mode 100644 index 000000000..7a6dca0a7 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.Blob-1.html @@ -0,0 +1 @@ +Blob | @wailsio/runtime
Blob: {
    prototype: Blob;
    new (blobParts?: BlobPart[], options?: BlobPropertyBag): Blob;
}

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.Event-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.Event-1.html new file mode 100644 index 000000000..10a91c579 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.Event-1.html @@ -0,0 +1 @@ +Event | @wailsio/runtime
Event: {
    AT_TARGET: 2;
    BUBBLING_PHASE: 3;
    CAPTURING_PHASE: 1;
    NONE: 0;
    prototype: Event;
    new (type: string, eventInitDict?: EventInit): Event;
}

Type declaration

  • ReadonlyAT_TARGET: 2
  • ReadonlyBUBBLING_PHASE: 3
  • ReadonlyCAPTURING_PHASE: 1
  • ReadonlyNONE: 0
  • prototype: Event
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.EventTarget-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.EventTarget-1.html new file mode 100644 index 000000000..b260ea11e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.EventTarget-1.html @@ -0,0 +1 @@ +EventTarget | @wailsio/runtime
EventTarget: { prototype: EventTarget; new (): EventTarget }

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.MediaSource-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.MediaSource-1.html new file mode 100644 index 000000000..ff20149ac --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.MediaSource-1.html @@ -0,0 +1,3 @@ +MediaSource | @wailsio/runtime
MediaSource: {
    canConstructInDedicatedWorker: boolean;
    prototype: MediaSource;
    isTypeSupported(type: string): boolean;
    new (): MediaSource;
}

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableByteStreamController-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableByteStreamController-1.html new file mode 100644 index 000000000..03abf22d0 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableByteStreamController-1.html @@ -0,0 +1 @@ +ReadableByteStreamController | @wailsio/runtime

Variable ReadableByteStreamController

ReadableByteStreamController: {
    prototype: ReadableByteStreamController;
    new (): ReadableByteStreamController;
}
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStream-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStream-1.html new file mode 100644 index 000000000..ff895225a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStream-1.html @@ -0,0 +1 @@ +ReadableStream | @wailsio/runtime
ReadableStream: {
    prototype: ReadableStream;
    new (
        underlyingSource: UnderlyingByteSource,
        strategy?: { highWaterMark?: number },
    ): ReadableStream<Uint8Array<ArrayBufferLike>>;
    new <R = any>(
        underlyingSource: UnderlyingDefaultSource<R>,
        strategy?: QueuingStrategy<R>,
    ): ReadableStream<R>;
    new <R = any>(
        underlyingSource?: UnderlyingSource<R>,
        strategy?: QueuingStrategy<R>,
    ): ReadableStream<R>;
}

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamBYOBReader-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamBYOBReader-1.html new file mode 100644 index 000000000..97d078bc2 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamBYOBReader-1.html @@ -0,0 +1 @@ +ReadableStreamBYOBReader | @wailsio/runtime
ReadableStreamBYOBReader: {
    prototype: ReadableStreamBYOBReader;
    new (
        stream: ReadableStream<Uint8Array<ArrayBufferLike>>,
    ): ReadableStreamBYOBReader;
}
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamBYOBRequest-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamBYOBRequest-1.html new file mode 100644 index 000000000..bcda606c6 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamBYOBRequest-1.html @@ -0,0 +1 @@ +ReadableStreamBYOBRequest | @wailsio/runtime

Variable ReadableStreamBYOBRequest

ReadableStreamBYOBRequest: {
    prototype: ReadableStreamBYOBRequest;
    new (): ReadableStreamBYOBRequest;
}
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamDefaultController-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamDefaultController-1.html new file mode 100644 index 000000000..b96ea63f8 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamDefaultController-1.html @@ -0,0 +1 @@ +ReadableStreamDefaultController | @wailsio/runtime

Variable ReadableStreamDefaultController

ReadableStreamDefaultController: {
    prototype: ReadableStreamDefaultController;
    new (): ReadableStreamDefaultController;
}
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamDefaultReader-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamDefaultReader-1.html new file mode 100644 index 000000000..a7d746f8d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.ReadableStreamDefaultReader-1.html @@ -0,0 +1 @@ +ReadableStreamDefaultReader | @wailsio/runtime

Variable ReadableStreamDefaultReader

ReadableStreamDefaultReader: {
    prototype: ReadableStreamDefaultReader;
    new <R = any>(stream: ReadableStream<R>): ReadableStreamDefaultReader<R>;
}

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.SourceBuffer-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.SourceBuffer-1.html new file mode 100644 index 000000000..ca7e84af9 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.SourceBuffer-1.html @@ -0,0 +1 @@ +SourceBuffer | @wailsio/runtime
SourceBuffer: { prototype: SourceBuffer; new (): SourceBuffer }

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.TimeRanges-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.TimeRanges-1.html new file mode 100644 index 000000000..9d9e6fa1b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.TimeRanges-1.html @@ -0,0 +1 @@ +TimeRanges | @wailsio/runtime
TimeRanges: { prototype: TimeRanges; new (): TimeRanges }

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.URL-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.URL-1.html new file mode 100644 index 000000000..a0f9adf7d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.URL-1.html @@ -0,0 +1,5 @@ +URL | @wailsio/runtime
URL: {
    prototype: URL;
    canParse(url: string | URL, base?: string | URL): boolean;
    createObjectURL(obj: Blob | MediaSource): string;
    parse(url: string | URL, base?: string | URL): null | URL;
    revokeObjectURL(url: string): void;
    new (url: string | URL, base?: string | URL): URL;
}

Type declaration

    • new (url: string | URL, base?: string | URL): URL
    • Parameters

      • url: string | URL
      • Optionalbase: string | URL

      Returns URL

  • prototype: URL
  • canParse:function
    • Parameters

      • url: string | URL
      • Optionalbase: string | URL

      Returns boolean

  • createObjectURL:function
  • parse:function
    • Parameters

      • url: string | URL
      • Optionalbase: string | URL

      Returns null | URL

  • revokeObjectURL:function
    • Parameters

      • url: string

      Returns void

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStream-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStream-1.html new file mode 100644 index 000000000..e31a06f97 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStream-1.html @@ -0,0 +1 @@ +WritableStream | @wailsio/runtime
WritableStream: {
    prototype: WritableStream;
    new <W = any>(
        underlyingSink?: UnderlyingSink<W>,
        strategy?: QueuingStrategy<W>,
    ): WritableStream<W>;
}

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStreamDefaultController-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStreamDefaultController-1.html new file mode 100644 index 000000000..98ec3ec27 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStreamDefaultController-1.html @@ -0,0 +1 @@ +WritableStreamDefaultController | @wailsio/runtime

Variable WritableStreamDefaultController

WritableStreamDefaultController: {
    prototype: WritableStreamDefaultController;
    new (): WritableStreamDefaultController;
}
diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStreamDefaultWriter-1.html b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStreamDefaultWriter-1.html new file mode 100644 index 000000000..286db85ef --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/docs/variables/_internal_.WritableStreamDefaultWriter-1.html @@ -0,0 +1 @@ +WritableStreamDefaultWriter | @wailsio/runtime

Variable WritableStreamDefaultWriter

WritableStreamDefaultWriter: {
    prototype: WritableStreamDefaultWriter;
    new <W = any>(stream: WritableStream<W>): WritableStreamDefaultWriter<W>;
}

Type declaration

diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/package-lock.json b/v3/internal/runtime/desktop/@wailsio/runtime/package-lock.json new file mode 100644 index 000000000..2c409024a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/package-lock.json @@ -0,0 +1,3140 @@ +{ + "name": "@wailsio/runtime", + "version": "3.0.0-alpha.68", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@wailsio/runtime", + "version": "3.0.0-alpha.68", + "license": "MIT", + "devDependencies": { + "happy-dom": "^17.1.1", + "promises-aplus-tests": "2.1.2", + "rimraf": "^5.0.5", + "typedoc": "^0.27.7", + "typedoc-plugin-markdown": "^4.4.2", + "typedoc-plugin-mdn-links": "^4.0.13", + "typedoc-plugin-missing-exports": "^3.1.0", + "typescript": "^5.7.3", + "vitest": "^3.0.6" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.27.2.tgz", + "integrity": "sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-oniguruma": "^1.27.2", + "@shikijs/types": "^1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", + "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", + "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", + "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", + "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", + "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", + "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", + "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", + "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", + "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", + "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", + "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", + "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", + "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", + "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", + "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", + "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", + "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", + "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", + "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz", + "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@shikijs/types": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz", + "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.7.tgz", + "integrity": "sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.7", + "@vitest/utils": "3.0.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.7.tgz", + "integrity": "sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.7.tgz", + "integrity": "sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.7.tgz", + "integrity": "sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.7", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.7.tgz", + "integrity": "sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.7.tgz", + "integrity": "sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.7.tgz", + "integrity": "sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.7", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/happy-dom": { + "version": "17.1.8", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.1.8.tgz", + "integrity": "sha512-Yxbq/FG79z1rhAf/iB6YM8wO2JB/JDQBy99RiLSs+2siEAi5J05x9eW1nnASHZJbpldjJE2KuFLsLZ+AzX/IxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0", + "whatwg-mimetype": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/promises-aplus-tests": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/promises-aplus-tests/-/promises-aplus-tests-2.1.2.tgz", + "integrity": "sha512-XiDfjQqx+rHLof8CU9xPOMLsjiXXxr3fkjE7WJjUzXttffB8K/nsnNsPTcwS4VvHliSjGVsYVqIjFeTHw53f5w==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "mocha": "^2.5.3", + "sinon": "^1.10.3", + "underscore": "~1.8.3" + }, + "bin": { + "promises-aplus-tests": "lib/cli.js" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", + "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.34.8", + "@rollup/rollup-android-arm64": "4.34.8", + "@rollup/rollup-darwin-arm64": "4.34.8", + "@rollup/rollup-darwin-x64": "4.34.8", + "@rollup/rollup-freebsd-arm64": "4.34.8", + "@rollup/rollup-freebsd-x64": "4.34.8", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", + "@rollup/rollup-linux-arm-musleabihf": "4.34.8", + "@rollup/rollup-linux-arm64-gnu": "4.34.8", + "@rollup/rollup-linux-arm64-musl": "4.34.8", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", + "@rollup/rollup-linux-riscv64-gnu": "4.34.8", + "@rollup/rollup-linux-s390x-gnu": "4.34.8", + "@rollup/rollup-linux-x64-gnu": "4.34.8", + "@rollup/rollup-linux-x64-musl": "4.34.8", + "@rollup/rollup-win32-arm64-msvc": "4.34.8", + "@rollup/rollup-win32-ia32-msvc": "4.34.8", + "@rollup/rollup-win32-x64-msvc": "4.34.8", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sinon": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz", + "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.2", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "nise": "^6.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typedoc": { + "version": "0.27.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.9.tgz", + "integrity": "sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.4.2.tgz", + "integrity": "sha512-kJVkU2Wd+AXQpyL6DlYXXRrfNrHrEIUgiABWH8Z+2Lz5Sq6an4dQ/hfvP75bbokjNDUskOdFlEEm/0fSVyC7eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.27.x" + } + }, + "node_modules/typedoc-plugin-mdn-links": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-4.0.14.tgz", + "integrity": "sha512-IDILzJr4OzNb5uAWWRMZYny80Q6jUQerAwskdYbNMwaHMRwsB3+y8oqYYE7/PyH+kJVvJnCC4G2wnAQ3CLdgqA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typedoc": "0.26.x || 0.27.x" + } + }, + "node_modules/typedoc-plugin-missing-exports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-3.1.0.tgz", + "integrity": "sha512-Sogbaj+qDa21NjB3SlIw4JXSwmcl/WOjwiPNaVEcPhpNG/MiRTtpwV81cT7h1cbu9StpONFPbddYWR0KV/fTWA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typedoc": "0.26.x || 0.27.x" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", + "integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.2", + "postcss": "^8.5.2", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.7.tgz", + "integrity": "sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.7.tgz", + "integrity": "sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.7", + "@vitest/mocker": "3.0.7", + "@vitest/pretty-format": "^3.0.7", + "@vitest/runner": "3.0.7", + "@vitest/snapshot": "3.0.7", + "@vitest/spy": "3.0.7", + "@vitest/utils": "3.0.7", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.7", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.7", + "@vitest/ui": "3.0.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/package.json b/v3/internal/runtime/desktop/@wailsio/runtime/package.json new file mode 100644 index 000000000..0a05e1a6c --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/package.json @@ -0,0 +1,62 @@ +{ + "name": "@wailsio/runtime", + "type": "module", + "version": "3.0.0-alpha.69", + "description": "Wails Runtime", + "types": "types/index.d.ts", + "exports": { + "types": "./types/index.d.ts", + "import": "./dist/index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wailsapp/wails.git", + "directory": "v3/internal/runtime/desktop/@wailsio/runtime" + }, + "author": "The Wails Team", + "license": "MIT", + "homepage": "https://v3.wails.io", + "bugs": { + "url": "https://github.com/wailsapp/wails/issues" + }, + "files": [ + "./dist", + "./types" + ], + "sideEffects": [ + "./dist/index.js", + "./dist/contextmenu.js", + "./dist/drag.js" + ], + "scripts": { + "check": "npx tsc --noEmit", + "test": "npx vitest run", + "clean": "npx rimraf ./dist ./docs ./types ./tsconfig.tsbuildinfo", + "generate:events": "task generate:events", + "generate": "npm run generate:events", + "prebuild": "npm run clean && npm run generate", + "build:code": "npx tsc", + "build:docs": "npx typedoc --gitRevision v3-alpha --plugin typedoc-plugin-mdn-links --plugin typedoc-plugin-missing-exports ./src/index.ts", + "build:docs:md": "npx typedoc --gitRevision v3-alpha --plugin typedoc-plugin-markdown --plugin typedoc-plugin-mdn-links --plugin typedoc-plugin-missing-exports ./src/index.ts", + "build": "npm run build:code & npm run build:docs & wait", + "prepack": "npm run build" + }, + "devDependencies": { + "happy-dom": "^17.1.1", + "promises-aplus-tests": "2.1.2", + "rimraf": "^5.0.5", + "typedoc": "^0.27.7", + "typedoc-plugin-markdown": "^4.4.2", + "typedoc-plugin-mdn-links": "^4.0.13", + "typedoc-plugin-missing-exports": "^3.1.0", + "typescript": "^5.7.3", + "vitest": "^3.0.6" + }, + "overrides": { + "promises-aplus-tests": { + "mocha": "^11.1.0", + "sinon": "^19.0.2", + "underscore": "^1.13.7" + } + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/application.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/application.ts new file mode 100644 index 000000000..57a41ac9e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/application.ts @@ -0,0 +1,37 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { newRuntimeCaller, objectNames } from "./runtime.js"; +const call = newRuntimeCaller(objectNames.Application); + +const HideMethod = 0; +const ShowMethod = 1; +const QuitMethod = 2; + +/** + * Hides a certain method by calling the HideMethod function. + */ +export function Hide(): Promise { + return call(HideMethod); +} + +/** + * Calls the ShowMethod and returns the result. + */ +export function Show(): Promise { + return call(ShowMethod); +} + +/** + * Calls the QuitMethod to terminate the program. + */ +export function Quit(): Promise { + return call(QuitMethod); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/browser.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/browser.ts new file mode 100644 index 000000000..465310d3d --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/browser.ts @@ -0,0 +1,24 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { newRuntimeCaller, objectNames } from "./runtime.js"; + +const call = newRuntimeCaller(objectNames.Browser); + +const BrowserOpenURL = 0; + +/** + * Open a browser window to the given URL. + * + * @param url - The URL to open + */ +export function OpenURL(url: string | URL): Promise { + return call(BrowserOpenURL, {url: url.toString()}); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/callable.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/callable.ts new file mode 100644 index 000000000..e8e2e4087 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/callable.ts @@ -0,0 +1,125 @@ +// Source: https://github.com/inspect-js/is-callable + +// The MIT License (MIT) +// +// Copyright (c) 2015 Jordan Harband +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +var fnToStr = Function.prototype.toString; +var reflectApply: typeof Reflect.apply | false | null = typeof Reflect === 'object' && Reflect !== null && Reflect.apply; +var badArrayLike: any; +var isCallableMarker: any; +if (typeof reflectApply === 'function' && typeof Object.defineProperty === 'function') { + try { + badArrayLike = Object.defineProperty({}, 'length', { + get: function () { + throw isCallableMarker; + } + }); + isCallableMarker = {}; + // eslint-disable-next-line no-throw-literal + reflectApply(function () { throw 42; }, null, badArrayLike); + } catch (_) { + if (_ !== isCallableMarker) { + reflectApply = null; + } + } +} else { + reflectApply = null; +} + +var constructorRegex = /^\s*class\b/; +var isES6ClassFn = function isES6ClassFunction(value: any): boolean { + try { + var fnStr = fnToStr.call(value); + return constructorRegex.test(fnStr); + } catch (e) { + return false; // not a function + } +}; + +var tryFunctionObject = function tryFunctionToStr(value: any): boolean { + try { + if (isES6ClassFn(value)) { return false; } + fnToStr.call(value); + return true; + } catch (e) { + return false; + } +}; +var toStr = Object.prototype.toString; +var objectClass = '[object Object]'; +var fnClass = '[object Function]'; +var genClass = '[object GeneratorFunction]'; +var ddaClass = '[object HTMLAllCollection]'; // IE 11 +var ddaClass2 = '[object HTML document.all class]'; +var ddaClass3 = '[object HTMLCollection]'; // IE 9-10 +var hasToStringTag = typeof Symbol === 'function' && !!Symbol.toStringTag; // better: use `has-tostringtag` + +var isIE68 = !(0 in [,]); // eslint-disable-line no-sparse-arrays, comma-spacing + +var isDDA: (value: any) => boolean = function isDocumentDotAll() { return false; }; +if (typeof document === 'object') { + // Firefox 3 canonicalizes DDA to undefined when it's not accessed directly + var all = document.all; + if (toStr.call(all) === toStr.call(document.all)) { + isDDA = function isDocumentDotAll(value) { + /* globals document: false */ + // in IE 6-8, typeof document.all is "object" and it's truthy + if ((isIE68 || !value) && (typeof value === 'undefined' || typeof value === 'object')) { + try { + var str = toStr.call(value); + return ( + str === ddaClass + || str === ddaClass2 + || str === ddaClass3 // opera 12.16 + || str === objectClass // IE 6-8 + ) && value('') == null; // eslint-disable-line eqeqeq + } catch (e) { /**/ } + } + return false; + }; + } +} + +function isCallableRefApply(value: T | unknown): value is (...args: any[]) => any { + if (isDDA(value)) { return true; } + if (!value) { return false; } + if (typeof value !== 'function' && typeof value !== 'object') { return false; } + try { + (reflectApply as any)(value, null, badArrayLike); + } catch (e) { + if (e !== isCallableMarker) { return false; } + } + return !isES6ClassFn(value) && tryFunctionObject(value); +} + +function isCallableNoRefApply(value: T | unknown): value is (...args: any[]) => any { + if (isDDA(value)) { return true; } + if (!value) { return false; } + if (typeof value !== 'function' && typeof value !== 'object') { return false; } + if (hasToStringTag) { return tryFunctionObject(value); } + if (isES6ClassFn(value)) { return false; } + var strClass = toStr.call(value); + if (strClass !== fnClass && strClass !== genClass && !(/^\[object HTML/).test(strClass)) { return false; } + return tryFunctionObject(value); +}; + +export default reflectApply ? isCallableRefApply : isCallableNoRefApply; diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/calls.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/calls.ts new file mode 100644 index 000000000..11d063323 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/calls.ts @@ -0,0 +1,233 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { CancellablePromise, type CancellablePromiseWithResolvers } from "./cancellable.js"; +import { newRuntimeCaller, objectNames } from "./runtime.js"; +import { nanoid } from "./nanoid.js"; + +// Setup +window._wails = window._wails || {}; +window._wails.callResultHandler = resultHandler; +window._wails.callErrorHandler = errorHandler; + +type PromiseResolvers = Omit, "promise" | "oncancelled"> + +const call = newRuntimeCaller(objectNames.Call); +const cancelCall = newRuntimeCaller(objectNames.CancelCall); +const callResponses = new Map(); + +const CallBinding = 0; +const CancelMethod = 0 + +/** + * Holds all required information for a binding call. + * May provide either a method ID or a method name, but not both. + */ +export type CallOptions = { + /** The numeric ID of the bound method to call. */ + methodID: number; + /** The fully qualified name of the bound method to call. */ + methodName?: never; + /** Arguments to be passed into the bound method. */ + args: any[]; +} | { + /** The numeric ID of the bound method to call. */ + methodID?: never; + /** The fully qualified name of the bound method to call. */ + methodName: string; + /** Arguments to be passed into the bound method. */ + args: any[]; +}; + +/** + * Exception class that will be thrown in case the bound method returns an error. + * The value of the {@link RuntimeError#name} property is "RuntimeError". + */ +export class RuntimeError extends Error { + /** + * Constructs a new RuntimeError instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message?: string, options?: ErrorOptions) { + super(message, options); + this.name = "RuntimeError"; + } +} + +/** + * Handles the result of a call request. + * + * @param id - The id of the request to handle the result for. + * @param data - The result data of the request. + * @param isJSON - Indicates whether the data is JSON or not. + */ +function resultHandler(id: string, data: string, isJSON: boolean): void { + const resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + + if (!data) { + resolvers.resolve(undefined); + } else if (!isJSON) { + resolvers.resolve(data); + } else { + try { + resolvers.resolve(JSON.parse(data)); + } catch (err: any) { + resolvers.reject(new TypeError("could not parse result: " + err.message, { cause: err })); + } + } +} + +/** + * Handles the error from a call request. + * + * @param id - The id of the promise handler. + * @param data - The error data to reject the promise handler with. + * @param isJSON - Indicates whether the data is JSON or not. + */ +function errorHandler(id: string, data: string, isJSON: boolean): void { + const resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + + if (!isJSON) { + resolvers.reject(new Error(data)); + } else { + let error: any; + try { + error = JSON.parse(data); + } catch (err: any) { + resolvers.reject(new TypeError("could not parse error: " + err.message, { cause: err })); + return; + } + + let options: ErrorOptions = {}; + if (error.cause) { + options.cause = error.cause; + } + + let exception; + switch (error.kind) { + case "ReferenceError": + exception = new ReferenceError(error.message, options); + break; + case "TypeError": + exception = new TypeError(error.message, options); + break; + case "RuntimeError": + exception = new RuntimeError(error.message, options); + break; + default: + exception = new Error(error.message, options); + break; + } + + resolvers.reject(exception); + } +} + +/** + * Retrieves and removes the response associated with the given ID from the callResponses map. + * + * @param id - The ID of the response to be retrieved and removed. + * @returns The response object associated with the given ID, if any. + */ +function getAndDeleteResponse(id: string): PromiseResolvers | undefined { + const response = callResponses.get(id); + callResponses.delete(id); + return response; +} + +/** + * Generates a unique ID using the nanoid library. + * + * @returns A unique ID that does not exist in the callResponses set. + */ +function generateID(): string { + let result; + do { + result = nanoid(); + } while (callResponses.has(result)); + return result; +} + +/** + * Call a bound method according to the given call options. + * + * In case of failure, the returned promise will reject with an exception + * among ReferenceError (unknown method), TypeError (wrong argument count or type), + * {@link RuntimeError} (method returned an error), or other (network or internal errors). + * The exception might have a "cause" field with the value returned + * by the application- or service-level error marshaling functions. + * + * @param options - A method call descriptor. + * @returns The result of the call. + */ +export function Call(options: CallOptions): CancellablePromise { + const id = generateID(); + + const result = CancellablePromise.withResolvers(); + callResponses.set(id, { resolve: result.resolve, reject: result.reject }); + + const request = call(CallBinding, Object.assign({ "call-id": id }, options)); + let running = false; + + request.then(() => { + running = true; + }, (err) => { + callResponses.delete(id); + result.reject(err); + }); + + const cancel = () => { + callResponses.delete(id); + return cancelCall(CancelMethod, {"call-id": id}).catch((err) => { + console.error("Error while requesting binding call cancellation:", err); + }); + }; + + result.oncancelled = () => { + if (running) { + return cancel(); + } else { + return request.then(cancel); + } + }; + + return result.promise; +} + +/** + * Calls a bound method by name with the specified arguments. + * See {@link Call} for details. + * + * @param methodName - The name of the method in the format 'package.struct.method'. + * @param args - The arguments to pass to the method. + * @returns The result of the method call. + */ +export function ByName(methodName: string, ...args: any[]): CancellablePromise { + return Call({ methodName, args }); +} + +/** + * Calls a method by its numeric ID with the specified arguments. + * See {@link Call} for details. + * + * @param methodID - The ID of the method to call. + * @param args - The arguments to pass to the method. + * @return The result of the method call. + */ +export function ByID(methodID: number, ...args: any[]): CancellablePromise { + return Call({ methodID, args }); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.test.js b/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.test.js new file mode 100644 index 000000000..b618c1459 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.test.js @@ -0,0 +1,431 @@ +import * as util from "node:util"; +import { describe, it, beforeEach, afterEach, assert, expect, vi } from "vitest"; +import { CancelError, CancellablePromise, CancelledRejectionError } from "./cancellable"; + +// TODO: In order of importance: +// TODO: test cancellation of subpromises the main promise resolves to. +// TODO: test cancellation of promise chains built by calling then() and friends: +// - all promises up the chain should be cancelled; +// - rejection handlers should be always executed with the CancelError of their parent promise in the chain; +// - promises returned from rejection handlers should be cancelled too; +// - if a rejection handler throws or returns a promise that ultimately rejects, +// it should be reported as an unhandled rejection, +// - unless it is a CancelError with the same reason given for cancelling the returned promise. +// TODO: test multiple calls to cancel() (second and later should have no effect). +// TODO: test static factory methods and their cancellation support. + +let expectedUnhandled = new Map(); + +process.on('unhandledRejection', function (error, promise) { + let reason = error; + if (reason instanceof CancelledRejectionError) { + promise = reason.promise; + reason = reason.cause; + } + + let reasons = expectedUnhandled.get(promise); + const callbacks = reasons?.get(reason); + if (callbacks) { + for (const cb of callbacks) { + try { + cb(reason, promise); + } catch (e) { + console.error("Exception in unhandled rejection callback.", e); + } + } + + reasons.delete(reason); + if (reasons.size === 0) { + expectedUnhandled.delete(promise); + } + return; + } + + console.log(util.format("Unhandled rejection.\nReason: %o\nPromise: %o", reason, promise)); + throw error; +}); + +function ignoreUnhandled(reason, promise) { + expectUnhandled(reason, promise, null); +} + +function expectUnhandled(reason, promise, cb) { + let reasons = expectedUnhandled.get(promise); + if (!reasons) { + reasons = new Map(); + expectedUnhandled.set(promise, reasons); + } + let callbacks = reasons.get(reason); + if (!callbacks) { + callbacks = []; + reasons.set(reason, callbacks); + } + if (cb) { + callbacks.push(cb); + } +} + +afterEach(() => { + vi.resetAllMocks(); + vi.restoreAllMocks(); +}); + +const dummyValue = { value: "value" }; +const dummyCause = { dummy: "dummy" }; +const dummyError = new Error("dummy"); +const oncancelled = vi.fn().mockName("oncancelled"); +const sentinel = vi.fn().mockName("sentinel"); +const unhandled = vi.fn().mockName("unhandled"); + +const resolutionPatterns = [ + ["forever", "pending", (test, value, { cls = CancellablePromise, cb = oncancelled } = {}) => test( + new cls(() => {}, cb) + )], + ["already", "fulfilled", (test, value, { cls = CancellablePromise, cb = oncancelled } = {}) => { + const prw = cls.withResolvers(); + prw.oncancelled = cb; + prw.resolve(value ?? dummyValue); + return test(prw.promise); + }], + ["immediately", "fulfilled", (test, value, { cls = CancellablePromise, cb = oncancelled } = {}) => { + const prw = cls.withResolvers(); + prw.oncancelled = cb; + const tp = test(prw.promise); + prw.resolve(value ?? dummyValue); + return tp; + }], + ["eventually", "fulfilled", async (test, value, { cls = CancellablePromise, cb = oncancelled } = {}) => { + const prw = cls.withResolvers(); + prw.oncancelled = cb; + const tp = test(prw.promise); + await new Promise((resolve) => { + setTimeout(() => { + prw.resolve(value ?? dummyValue); + resolve(); + }, 50); + }); + return tp; + }], + ["already", "rejected", (test, reason, { cls = CancellablePromise, cb = oncancelled } = {}) => { + const prw = cls.withResolvers(); + prw.oncancelled = cb; + prw.reject(reason ?? dummyError); + return test(prw.promise); + }], + ["immediately", "rejected", (test, reason, { cls = CancellablePromise, cb = oncancelled } = {}) => { + const prw = cls.withResolvers(); + prw.oncancelled = cb; + const tp = test(prw.promise); + prw.reject(reason ?? dummyError); + return tp; + }], + ["eventually", "rejected", async (test, reason, { cls = CancellablePromise, cb = oncancelled } = {}) => { + const prw = cls.withResolvers(); + prw.oncancelled = cb; + const tp = test(prw.promise); + await new Promise((resolve) => { + setTimeout(() => { + prw.reject(reason ?? dummyError); + resolve(); + }, 50); + }); + return tp; + }], +]; + +describe("CancellablePromise.cancel", ()=> { + it("should suppress its own unhandled cancellation error", async () => { + const p = new CancellablePromise(() => {}); + p.cancel(); + + process.on('unhandledRejection', sentinel); + await new Promise((resolve) => setTimeout(resolve, 100)); + process.off('unhandledRejection', sentinel); + + expect(sentinel).not.toHaveBeenCalled(); + }); + + it.for([ + ["rejections", dummyError], + ["cancellation errors", new CancelError("dummy", { cause: dummyCause })], + ])("should not suppress arbitrary unhandled %s", async ([kind, err]) => { + const p = new CancellablePromise(() => { throw err; }); + p.cancel(); + + await new Promise((resolve) => { + expectUnhandled(err, p, unhandled); + expectUnhandled(err, p, resolve); + }); + + expect(unhandled).toHaveBeenCalledExactlyOnceWith(err, p); + }); + + describe.for(resolutionPatterns)("when applied to %s %s promises", ([time, state, test]) => { + if (time === "already") { + it("should have no effect", () => test(async (promise) => { + promise.then(sentinel, sentinel); + + let reason; + try { + promise.cancel(); + await promise; + assert(state === "fulfilled", "Promise fulfilled unexpectedly"); + } catch (err) { + reason = err; + assert(state === "rejected", "Promise rejected unexpectedly"); + } + + expect(sentinel).toHaveBeenCalled(); + expect(oncancelled).not.toHaveBeenCalled(); + expect(reason).not.toBeInstanceOf(CancelError); + })); + } else { + if (state === "rejected") { + it("should report late rejections as unhandled", () => test(async (promise) => { + promise.cancel(); + + await new Promise((resolve) => { + expectUnhandled(dummyError, promise, unhandled); + expectUnhandled(dummyError, promise, resolve); + }); + + expect(unhandled).toHaveBeenCalledExactlyOnceWith(dummyError, promise); + })); + } + + it("should reject with a CancelError", () => test(async (promise) => { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + let reason; + try { + promise.cancel(); + await promise; + } catch (err) { + reason = err; + } + + expect(reason).toBeInstanceOf(CancelError); + })); + + it("should call the oncancelled callback synchronously", () => test(async (promise) => { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + try { + promise.cancel(); + sentinel(); + await promise; + } catch {} + + expect(oncancelled).toHaveBeenCalledBefore(sentinel); + })); + + it("should propagate the given cause", () => test(async (promise) => { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + let reason; + try { + promise.cancel(dummyCause); + await promise; + } catch (err) { + reason = err; + } + + expect(reason).toBeInstanceOf(CancelError); + expect(reason).toHaveProperty('cause', dummyCause); + expect(oncancelled).toHaveBeenCalledWith(reason.cause); + })); + } + }); +}); + +const onabort = vi.fn().mockName("abort"); + +const abortPatterns = [ + ["never", "standalone", (test) => { + const signal = new AbortSignal(); + signal.addEventListener('abort', onabort, { capture: true }); + return test(signal); + }], + ["already", "standalone", (test) => { + const signal = AbortSignal.abort(dummyCause); + onabort(); + return test(signal); + }], + ["eventually", "standalone", (test) => { + const signal = AbortSignal.timeout(25); + signal.addEventListener('abort', onabort, { capture: true }); + return test(signal); + }], + ["never", "controller-bound", (test) => { + const signal = new AbortController().signal; + signal.addEventListener('abort', onabort, { capture: true }); + return test(signal); + }], + ["already", " controller-bound", (test) => { + const ctrl = new AbortController(); + ctrl.signal.addEventListener('abort', onabort, { capture: true }); + ctrl.abort(dummyCause); + return test(ctrl.signal); + }], + ["immediately", "controller-bound", (test) => { + const ctrl = new AbortController(); + ctrl.signal.addEventListener('abort', onabort, { capture: true }); + const tp = test(ctrl.signal); + ctrl.abort(dummyCause); + return tp; + }], + ["eventually", "controller-bound", (test) => { + const ctrl = new AbortController(); + ctrl.signal.addEventListener('abort', onabort, { capture: true }); + const tp = test(ctrl.signal); + setTimeout(() => ctrl.abort(dummyCause), 25); + return tp; + }] +]; + +describe("CancellablePromise.cancelOn", ()=> { + it("should return the target promise for chaining", () => { + const p = new CancellablePromise(() => {}); + expect(p.cancelOn(AbortSignal.abort())).toBe(p); + }); + + function tests(abortTime, mode, testSignal, resolveTime, state, testPromise) { + if (abortTime !== "never") { + it(`should call CancellablePromise.cancel ${abortTime === "already" ? "immediately" : "on abort"} with the abort reason as cause`, () => testSignal((signal) => testPromise(async (promise) => { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + const cancelSpy = vi.spyOn(promise, 'cancel'); + + promise.catch(() => {}); + promise.cancelOn(signal); + + if (signal.aborted) { + sentinel(); + } else { + await new Promise((resolve) => { + signal.onabort = () => { + sentinel(); + resolve(); + }; + }); + } + + expect(cancelSpy).toHaveBeenCalledAfter(onabort); + expect(cancelSpy).toHaveBeenCalledBefore(sentinel); + expect(cancelSpy).toHaveBeenCalledExactlyOnceWith(signal.reason); + }))); + } + + if ( + resolveTime === "already" + || abortTime === "never" + || ( + ["immediately", "eventually"].includes(abortTime) + && ["already", "immediately"].includes(resolveTime) + ) + ) { + it("should have no effect", () => testSignal((signal) => testPromise(async (promise) => { + promise.then(sentinel, sentinel); + + let reason; + try { + if (resolveTime !== "forever") { + await promise.cancelOn(signal); + assert(state === "fulfilled", "Promise fulfilled unexpectedly"); + } else { + await Promise.race([promise, new Promise((resolve) => setTimeout(resolve, 100))]).then(sentinel); + } + } catch (err) { + reason = err; + assert(state === "rejected", "Promise rejected unexpectedly"); + } + + if (abortTime !== "never" && !signal.aborted) { + // Wait for the AbortSignal to have actually aborted. + await new Promise((resolve) => signal.onabort = resolve); + } + + expect(sentinel).toHaveBeenCalled(); + expect(oncancelled).not.toHaveBeenCalled(); + expect(reason).not.toBeInstanceOf(CancelError); + }))); + } else { + if (state === "rejected") { + it("should report late rejections as unhandled", () => testSignal((signal) => testPromise(async (promise) => { + promise.cancelOn(signal); + + await new Promise((resolve) => { + expectUnhandled(dummyError, promise, unhandled); + expectUnhandled(dummyError, promise, resolve); + }); + + expect(unhandled).toHaveBeenCalledExactlyOnceWith(dummyError, promise); + }))); + } + + it("should reject with a CancelError", () => testSignal((signal) => testPromise(async (promise)=> { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + let reason; + try { + await promise.cancelOn(signal); + } catch (err) { + reason = err; + } + + expect(reason).toBeInstanceOf(CancelError); + }))); + + it(`should call the oncancelled callback ${abortTime === "already" ? "" : "a"}synchronously`, () => testSignal((signal) => testPromise(async (promise) => { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + try { + promise.cancelOn(signal); + sentinel(); + await promise; + } catch {} + + expect(oncancelled).toHaveBeenCalledAfter(onabort); + if (abortTime === "already") { + expect(oncancelled).toHaveBeenCalledBefore(sentinel); + } else { + expect(oncancelled).toHaveBeenCalledAfter(sentinel); + } + }))); + + it("should propagate the abort reason as cause", () => testSignal((signal) => testPromise(async (promise) => { + // Ignore the unhandled rejection from the test promise. + if (state === "rejected") { ignoreUnhandled(dummyError, promise); } + + let reason; + try { + await promise.cancelOn(signal); + } catch (err) { + reason = err; + } + + expect(reason).toBeInstanceOf(CancelError); + expect(reason).toHaveProperty('cause', signal.reason); + expect(oncancelled).toHaveBeenCalledWith(signal.reason); + }))); + } + } + + describe.for(abortPatterns)("when called with %s aborted %s signals", ([abortTime, mode, testSignal]) => { + describe.for(resolutionPatterns)("when applied to %s %s promises", ([resolveTime, state, testPromise]) => { + tests(abortTime, mode, testSignal, resolveTime, state, testPromise); + }); + }); + + describe.for(resolutionPatterns)("when applied to %s %s promises", ([resolveTime, state, testPromise]) => { + describe.for(abortPatterns)("when called with %s aborted %s signals", ([abortTime, mode, testSignal]) => { + tests(abortTime, mode, testSignal, resolveTime, state, testPromise); + }); + }); +}); \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts new file mode 100644 index 000000000..5a839e211 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts @@ -0,0 +1,934 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import isCallable from "./callable.js"; + +/** + * Exception class that will be used as rejection reason + * in case a {@link CancellablePromise} is cancelled successfully. + * + * The value of the {@link name} property is the string `"CancelError"`. + * The value of the {@link cause} property is the cause passed to the cancel method, if any. + */ +export class CancelError extends Error { + /** + * Constructs a new `CancelError` instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message?: string, options?: ErrorOptions) { + super(message, options); + this.name = "CancelError"; + } +} + +/** + * Exception class that will be reported as an unhandled rejection + * in case a {@link CancellablePromise} rejects after being cancelled, + * or when the `oncancelled` callback throws or rejects. + * + * The value of the {@link name} property is the string `"CancelledRejectionError"`. + * The value of the {@link cause} property is the reason the promise rejected with. + * + * Because the original promise was cancelled, + * a wrapper promise will be passed to the unhandled rejection listener instead. + * The {@link promise} property holds a reference to the original promise. + */ +export class CancelledRejectionError extends Error { + /** + * Holds a reference to the promise that was cancelled and then rejected. + */ + promise: CancellablePromise; + + /** + * Constructs a new `CancelledRejectionError` instance. + * @param promise - The promise that caused the error originally. + * @param reason - The rejection reason. + * @param info - An optional informative message specifying the circumstances in which the error was thrown. + * Defaults to the string `"Unhandled rejection in cancelled promise."`. + */ + constructor(promise: CancellablePromise, reason?: any, info?: string) { + super((info ?? "Unhandled rejection in cancelled promise.") + " Reason: " + errorMessage(reason), { cause: reason }); + this.promise = promise; + this.name = "CancelledRejectionError"; + } +} + +type CancellablePromiseResolver = (value: T | PromiseLike | CancellablePromiseLike) => void; +type CancellablePromiseRejector = (reason?: any) => void; +type CancellablePromiseCanceller = (cause?: any) => void | PromiseLike; +type CancellablePromiseExecutor = (resolve: CancellablePromiseResolver, reject: CancellablePromiseRejector) => void; + +export interface CancellablePromiseLike { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike | CancellablePromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike | CancellablePromiseLike) | undefined | null): CancellablePromiseLike; + cancel(cause?: any): void | PromiseLike; +} + +/** + * Wraps a cancellable promise along with its resolution methods. + * The `oncancelled` field will be null initially but may be set to provide a custom cancellation function. + */ +export interface CancellablePromiseWithResolvers { + promise: CancellablePromise; + resolve: CancellablePromiseResolver; + reject: CancellablePromiseRejector; + oncancelled: CancellablePromiseCanceller | null; +} + +interface CancellablePromiseState { + readonly root: CancellablePromiseState; + resolving: boolean; + settled: boolean; + reason?: CancelError; +} + +// Private field names. +const barrierSym = Symbol("barrier"); +const cancelImplSym = Symbol("cancelImpl"); +const species = Symbol.species ?? Symbol("speciesPolyfill"); + +/** + * A promise with an attached method for cancelling long-running operations (see {@link CancellablePromise#cancel}). + * Cancellation can optionally be bound to an {@link AbortSignal} + * for better composability (see {@link CancellablePromise#cancelOn}). + * + * Cancelling a pending promise will result in an immediate rejection + * with an instance of {@link CancelError} as reason, + * but whoever started the promise will be responsible + * for actually aborting the underlying operation. + * To this purpose, the constructor and all chaining methods + * accept optional cancellation callbacks. + * + * If a `CancellablePromise` still resolves after having been cancelled, + * the result will be discarded. If it rejects, the reason + * will be reported as an unhandled rejection, + * wrapped in a {@link CancelledRejectionError} instance. + * To facilitate the handling of cancellation requests, + * cancelled `CancellablePromise`s will _not_ report unhandled `CancelError`s + * whose `cause` field is the same as the one with which the current promise was cancelled. + * + * All usual promise methods are defined and return a `CancellablePromise` + * whose cancel method will cancel the parent operation as well, propagating the cancellation reason + * upwards through promise chains. + * Conversely, cancelling a promise will not automatically cancel dependent promises downstream: + * ```ts + * let root = new CancellablePromise((resolve, reject) => { ... }); + * let child1 = root.then(() => { ... }); + * let child2 = child1.then(() => { ... }); + * let child3 = root.catch(() => { ... }); + * child1.cancel(); // Cancels child1 and root, but not child2 or child3 + * ``` + * Cancelling a promise that has already settled is safe and has no consequence. + * + * The `cancel` method returns a promise that _always fulfills_ + * after the whole chain has processed the cancel request + * and all attached callbacks up to that moment have run. + * + * All ES2024 promise methods (static and instance) are defined on CancellablePromise, + * but actual availability may vary with OS/webview version. + * + * In line with the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing, + * `CancellablePromise` does not support transparent subclassing. + * Extenders should take care to provide their own method implementations. + * This might be reconsidered in case the proposal is retired. + * + * CancellablePromise is a wrapper around the DOM Promise object + * and is compliant with the [Promises/A+ specification](https://promisesaplus.com/) + * (it passes the [compliance suite](https://github.com/promises-aplus/promises-tests)) + * if so is the underlying implementation. + */ +export class CancellablePromise extends Promise implements PromiseLike, CancellablePromiseLike { + // Private fields. + /** @internal */ + private [barrierSym]!: Partial> | null; + /** @internal */ + private readonly [cancelImplSym]!: (reason: CancelError) => void | PromiseLike; + + /** + * Creates a new `CancellablePromise`. + * + * @param executor - A callback used to initialize the promise. This callback is passed two arguments: + * a `resolve` callback used to resolve the promise with a value + * or the result of another promise (possibly cancellable), + * and a `reject` callback used to reject the promise with a provided reason or error. + * If the value provided to the `resolve` callback is a thenable _and_ cancellable object + * (it has a `then` _and_ a `cancel` method), + * cancellation requests will be forwarded to that object and the oncancelled will not be invoked anymore. + * If any one of the two callbacks is called _after_ the promise has been cancelled, + * the provided values will be cancelled and resolved as usual, + * but their results will be discarded. + * However, if the resolution process ultimately ends up in a rejection + * that is not due to cancellation, the rejection reason + * will be wrapped in a {@link CancelledRejectionError} + * and bubbled up as an unhandled rejection. + * @param oncancelled - It is the caller's responsibility to ensure that any operation + * started by the executor is properly halted upon cancellation. + * This optional callback can be used to that purpose. + * It will be called _synchronously_ with a cancellation cause + * when cancellation is requested, _after_ the promise has already rejected + * with a {@link CancelError}, but _before_ + * any {@link then}/{@link catch}/{@link finally} callback runs. + * If the callback returns a thenable, the promise returned from {@link cancel} + * will only fulfill after the former has settled. + * Unhandled exceptions or rejections from the callback will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as unhandled rejections. + * If the `resolve` callback is called before cancellation with a cancellable promise, + * cancellation requests on this promise will be diverted to that promise, + * and the original `oncancelled` callback will be discarded. + */ + constructor(executor: CancellablePromiseExecutor, oncancelled?: CancellablePromiseCanceller) { + let resolve!: (value: T | PromiseLike) => void; + let reject!: (reason?: any) => void; + super((res, rej) => { resolve = res; reject = rej; }); + + if ((this.constructor as any)[species] !== Promise) { + throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property."); + } + + let promise: CancellablePromiseWithResolvers = { + promise: this, + resolve, + reject, + get oncancelled() { return oncancelled ?? null; }, + set oncancelled(cb) { oncancelled = cb ?? undefined; } + }; + + const state: CancellablePromiseState = { + get root() { return state; }, + resolving: false, + settled: false + }; + + // Setup cancellation system. + void Object.defineProperties(this, { + [barrierSym]: { + configurable: false, + enumerable: false, + writable: true, + value: null + }, + [cancelImplSym]: { + configurable: false, + enumerable: false, + writable: false, + value: cancellerFor(promise, state) + } + }); + + // Run the actual executor. + const rejector = rejectorFor(promise, state); + try { + executor(resolverFor(promise, state), rejector); + } catch (err) { + if (state.resolving) { + console.log("Unhandled exception in CancellablePromise executor.", err); + } else { + rejector(err); + } + } + } + + /** + * Cancels immediately the execution of the operation associated with this promise. + * The promise rejects with a {@link CancelError} instance as reason, + * with the {@link CancelError#cause} property set to the given argument, if any. + * + * Has no effect if called after the promise has already settled; + * repeated calls in particular are safe, but only the first one + * will set the cancellation cause. + * + * The `CancelError` exception _need not_ be handled explicitly _on the promises that are being cancelled:_ + * cancelling a promise with no attached rejection handler does not trigger an unhandled rejection event. + * Therefore, the following idioms are all equally correct: + * ```ts + * new CancellablePromise((resolve, reject) => { ... }).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).catch(...).cancel(); + * ``` + * Whenever some cancelled promise in a chain rejects with a `CancelError` + * with the same cancellation cause as itself, the error will be discarded silently. + * However, the `CancelError` _will still be delivered_ to all attached rejection handlers + * added by {@link then} and related methods: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * cancellable.then(() => { ... }).catch(console.log); + * cancellable.cancel(); // A CancelError is printed to the console. + * ``` + * If the `CancelError` is not handled downstream by the time it reaches + * a _non-cancelled_ promise, it _will_ trigger an unhandled rejection event, + * just like normal rejections would: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * let chained = cancellable.then(() => { ... }).then(() => { ... }); // No catch... + * cancellable.cancel(); // Unhandled rejection event on chained! + * ``` + * Therefore, it is important to either cancel whole promise chains from their tail, + * as shown in the correct idioms above, or take care of handling errors everywhere. + * + * @returns A cancellable promise that _fulfills_ after the cancel callback (if any) + * and all handlers attached up to the call to cancel have run. + * If the cancel callback returns a thenable, the promise returned by `cancel` + * will also wait for that thenable to settle. + * This enables callers to wait for the cancelled operation to terminate + * without being forced to handle potential errors at the call site. + * ```ts + * cancellable.cancel().then(() => { + * // Cleanup finished, it's safe to do something else. + * }, (err) => { + * // Unreachable: the promise returned from cancel will never reject. + * }); + * ``` + * Note that the returned promise will _not_ handle implicitly any rejection + * that might have occurred already in the cancelled chain. + * It will just track whether registered handlers have been executed or not. + * Therefore, unhandled rejections will never be silently handled by calling cancel. + */ + cancel(cause?: any): CancellablePromise { + return new CancellablePromise((resolve) => { + // INVARIANT: the result of this[cancelImplSym] and the barrier do not ever reject. + // Unfortunately macOS High Sierra does not support Promise.allSettled. + Promise.all([ + this[cancelImplSym](new CancelError("Promise cancelled.", { cause })), + currentBarrier(this) + ]).then(() => resolve(), () => resolve()); + }); + } + + /** + * Binds promise cancellation to the abort event of the given {@link AbortSignal}. + * If the signal has already aborted, the promise will be cancelled immediately. + * When either condition is verified, the cancellation cause will be set + * to the signal's abort reason (see {@link AbortSignal#reason}). + * + * Has no effect if called (or if the signal aborts) _after_ the promise has already settled. + * Only the first signal to abort will set the cancellation cause. + * + * For more details about the cancellation process, + * see {@link cancel} and the `CancellablePromise` constructor. + * + * This method enables `await`ing cancellable promises without having + * to store them for future cancellation, e.g.: + * ```ts + * await longRunningOperation().cancelOn(signal); + * ``` + * instead of: + * ```ts + * let promiseToBeCancelled = longRunningOperation(); + * await promiseToBeCancelled; + * ``` + * + * @returns This promise, for method chaining. + */ + cancelOn(signal: AbortSignal): CancellablePromise { + if (signal.aborted) { + void this.cancel(signal.reason) + } else { + signal.addEventListener('abort', () => void this.cancel(signal.reason), {capture: true}); + } + + return this; + } + + /** + * Attaches callbacks for the resolution and/or rejection of the `CancellablePromise`. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A `CancellablePromise` for the completion of whichever callback is executed. + * The returned promise is hooked up to propagate cancellation requests up the chain, but not down: + * + * - if the parent promise is cancelled, the `onrejected` handler will be invoked with a `CancelError` + * and the returned promise _will resolve regularly_ with its result; + * - conversely, if the returned promise is cancelled, _the parent promise is cancelled too;_ + * the `onrejected` handler will still be invoked with the parent's `CancelError`, + * but its result will be discarded + * and the returned promise will reject with a `CancelError` as well. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If either callback returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike | CancellablePromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike | CancellablePromiseLike) | undefined | null, oncancelled?: CancellablePromiseCanceller): CancellablePromise { + if (!(this instanceof CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.then called on an invalid object."); + } + + // NOTE: TypeScript's built-in type for then is broken, + // as it allows specifying an arbitrary TResult1 != T even when onfulfilled is not a function. + // We cannot fix it if we want to CancellablePromise to implement PromiseLike. + + if (!isCallable(onfulfilled)) { onfulfilled = identity as any; } + if (!isCallable(onrejected)) { onrejected = thrower; } + + if (onfulfilled === identity && onrejected == thrower) { + // Shortcut for trivial arguments. + return new CancellablePromise((resolve) => resolve(this as any)); + } + + const barrier: Partial> = {}; + this[barrierSym] = barrier; + + return new CancellablePromise((resolve, reject) => { + void super.then( + (value) => { + if (this[barrierSym] === barrier) { this[barrierSym] = null; } + barrier.resolve?.(); + + try { + resolve(onfulfilled!(value)); + } catch (err) { + reject(err); + } + }, + (reason?) => { + if (this[barrierSym] === barrier) { this[barrierSym] = null; } + barrier.resolve?.(); + + try { + resolve(onrejected!(reason)); + } catch (err) { + reject(err); + } + } + ); + }, async (cause?) => { + //cancelled = true; + try { + return oncancelled?.(cause); + } finally { + await this.cancel(cause); + } + }); + } + + /** + * Attaches a callback for only the rejection of the Promise. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * It is equivalent to + * ```ts + * cancellablePromise.then(undefined, onrejected, oncancelled); + * ``` + * and the same caveats apply. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onrejected` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + catch(onrejected?: ((reason: any) => (PromiseLike | TResult)) | undefined | null, oncancelled?: CancellablePromiseCanceller): CancellablePromise { + return this.then(undefined, onrejected, oncancelled); + } + + /** + * Attaches a callback that is invoked when the CancellablePromise is settled (fulfilled or rejected). The + * resolved value cannot be accessed or modified from the callback. + * The returned promise will settle in the same state as the original one + * after the provided callback has completed execution, + * unless the callback throws or returns a rejecting promise, + * in which case the returned promise will reject as well. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * Once the parent promise settles, the `onfinally` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * This method is implemented in terms of {@link then} and the same caveats apply. + * It is polyfilled, hence available in every OS/webview version. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onfinally` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + finally(onfinally?: (() => void) | undefined | null, oncancelled?: CancellablePromiseCanceller): CancellablePromise { + if (!(this instanceof CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.finally called on an invalid object."); + } + + if (!isCallable(onfinally)) { + return this.then(onfinally, onfinally, oncancelled); + } + + return this.then( + (value) => CancellablePromise.resolve(onfinally()).then(() => value), + (reason?) => CancellablePromise.resolve(onfinally()).then(() => { throw reason; }), + oncancelled, + ); + } + + /** + * We use the `[Symbol.species]` static property, if available, + * to disable the built-in automatic subclassing features from {@link Promise}. + * It is critical for performance reasons that extenders do not override this. + * Once the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing + * is either accepted or retired, this implementation will have to be revised accordingly. + * + * @ignore + * @internal + */ + static get [species]() { + return Promise; + } + + /** + * Creates a CancellablePromise that is resolved with an array of results + * when all of the provided Promises resolve, or rejected when any Promise is rejected. + * + * Every one of the provided objects that is a thenable _and_ cancellable object + * will be cancelled when the returned promise is cancelled, with the same cause. + * + * @group Static Methods + */ + static all(values: Iterable>): CancellablePromise[]>; + static all(values: T): CancellablePromise<{ -readonly [P in keyof T]: Awaited; }>; + static all | ArrayLike>(values: T): CancellablePromise { + let collected = Array.from(values); + const promise = collected.length === 0 + ? CancellablePromise.resolve(collected) + : new CancellablePromise((resolve, reject) => { + void Promise.all(collected).then(resolve, reject); + }, (cause?): Promise => cancelAll(promise, collected, cause)); + return promise; + } + + /** + * Creates a CancellablePromise that is resolved with an array of results + * when all of the provided Promises resolve or reject. + * + * Every one of the provided objects that is a thenable _and_ cancellable object + * will be cancelled when the returned promise is cancelled, with the same cause. + * + * @group Static Methods + */ + static allSettled(values: Iterable>): CancellablePromise>[]>; + static allSettled(values: T): CancellablePromise<{ -readonly [P in keyof T]: PromiseSettledResult>; }>; + static allSettled | ArrayLike>(values: T): CancellablePromise { + let collected = Array.from(values); + const promise = collected.length === 0 + ? CancellablePromise.resolve(collected) + : new CancellablePromise((resolve, reject) => { + void Promise.allSettled(collected).then(resolve, reject); + }, (cause?): Promise => cancelAll(promise, collected, cause)); + return promise; + } + + /** + * The any function returns a promise that is fulfilled by the first given promise to be fulfilled, + * or rejected with an AggregateError containing an array of rejection reasons + * if all of the given promises are rejected. + * It resolves all elements of the passed iterable to promises as it runs this algorithm. + * + * Every one of the provided objects that is a thenable _and_ cancellable object + * will be cancelled when the returned promise is cancelled, with the same cause. + * + * @group Static Methods + */ + static any(values: Iterable>): CancellablePromise>; + static any(values: T): CancellablePromise>; + static any | ArrayLike>(values: T): CancellablePromise { + let collected = Array.from(values); + const promise = collected.length === 0 + ? CancellablePromise.resolve(collected) + : new CancellablePromise((resolve, reject) => { + void Promise.any(collected).then(resolve, reject); + }, (cause?): Promise => cancelAll(promise, collected, cause)); + return promise; + } + + /** + * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved or rejected. + * + * Every one of the provided objects that is a thenable _and_ cancellable object + * will be cancelled when the returned promise is cancelled, with the same cause. + * + * @group Static Methods + */ + static race(values: Iterable>): CancellablePromise>; + static race(values: T): CancellablePromise>; + static race | ArrayLike>(values: T): CancellablePromise { + let collected = Array.from(values); + const promise = new CancellablePromise((resolve, reject) => { + void Promise.race(collected).then(resolve, reject); + }, (cause?): Promise => cancelAll(promise, collected, cause)); + return promise; + } + + /** + * Creates a new cancelled CancellablePromise for the provided cause. + * + * @group Static Methods + */ + static cancel(cause?: any): CancellablePromise { + const p = new CancellablePromise(() => {}); + p.cancel(cause); + return p; + } + + /** + * Creates a new CancellablePromise that cancels + * after the specified timeout, with the provided cause. + * + * If the {@link AbortSignal.timeout} factory method is available, + * it is used to base the timeout on _active_ time rather than _elapsed_ time. + * Otherwise, `timeout` falls back to {@link setTimeout}. + * + * @group Static Methods + */ + static timeout(milliseconds: number, cause?: any): CancellablePromise { + const promise = new CancellablePromise(() => {}); + if (AbortSignal && typeof AbortSignal === 'function' && AbortSignal.timeout && typeof AbortSignal.timeout === 'function') { + AbortSignal.timeout(milliseconds).addEventListener('abort', () => void promise.cancel(cause)); + } else { + setTimeout(() => void promise.cancel(cause), milliseconds); + } + return promise; + } + + /** + * Creates a new CancellablePromise that resolves after the specified timeout. + * The returned promise can be cancelled without consequences. + * + * @group Static Methods + */ + static sleep(milliseconds: number): CancellablePromise; + /** + * Creates a new CancellablePromise that resolves after + * the specified timeout, with the provided value. + * The returned promise can be cancelled without consequences. + * + * @group Static Methods + */ + static sleep(milliseconds: number, value: T): CancellablePromise; + static sleep(milliseconds: number, value?: T): CancellablePromise { + return new CancellablePromise((resolve) => { + setTimeout(() => resolve(value!), milliseconds); + }); + } + + /** + * Creates a new rejected CancellablePromise for the provided reason. + * + * @group Static Methods + */ + static reject(reason?: any): CancellablePromise { + return new CancellablePromise((_, reject) => reject(reason)); + } + + /** + * Creates a new resolved CancellablePromise. + * + * @group Static Methods + */ + static resolve(): CancellablePromise; + /** + * Creates a new resolved CancellablePromise for the provided value. + * + * @group Static Methods + */ + static resolve(value: T): CancellablePromise>; + /** + * Creates a new resolved CancellablePromise for the provided value. + * + * @group Static Methods + */ + static resolve(value: T | PromiseLike): CancellablePromise>; + static resolve(value?: T | PromiseLike): CancellablePromise> { + if (value instanceof CancellablePromise) { + // Optimise for cancellable promises. + return value; + } + return new CancellablePromise((resolve) => resolve(value)); + } + + /** + * Creates a new CancellablePromise and returns it in an object, along with its resolve and reject functions + * and a getter/setter for the cancellation callback. + * + * This method is polyfilled, hence available in every OS/webview version. + * + * @group Static Methods + */ + static withResolvers(): CancellablePromiseWithResolvers { + let result: CancellablePromiseWithResolvers = { oncancelled: null } as any; + result.promise = new CancellablePromise((resolve, reject) => { + result.resolve = resolve; + result.reject = reject; + }, (cause?: any) => { result.oncancelled?.(cause); }); + return result; + } +} + +/** + * Returns a callback that implements the cancellation algorithm for the given cancellable promise. + * The promise returned from the resulting function does not reject. + */ +function cancellerFor(promise: CancellablePromiseWithResolvers, state: CancellablePromiseState) { + let cancellationPromise: void | PromiseLike = undefined; + + return (reason: CancelError): void | PromiseLike => { + if (!state.settled) { + state.settled = true; + state.reason = reason; + promise.reject(reason); + + // Attach an error handler that ignores this specific rejection reason and nothing else. + // In theory, a sane underlying implementation at this point + // should always reject with our cancellation reason, + // hence the handler will never throw. + void Promise.prototype.then.call(promise.promise, undefined, (err) => { + if (err !== reason) { + throw err; + } + }); + } + + // If reason is not set, the promise resolved regularly, hence we must not call oncancelled. + // If oncancelled is unset, no need to go any further. + if (!state.reason || !promise.oncancelled) { return; } + + cancellationPromise = new Promise((resolve) => { + try { + resolve(promise.oncancelled!(state.reason!.cause)); + } catch (err) { + Promise.reject(new CancelledRejectionError(promise.promise, err, "Unhandled exception in oncancelled callback.")); + } + }).catch((reason?) => { + Promise.reject(new CancelledRejectionError(promise.promise, reason, "Unhandled rejection in oncancelled callback.")); + }); + + // Unset oncancelled to prevent repeated calls. + promise.oncancelled = null; + + return cancellationPromise; + } +} + +/** + * Returns a callback that implements the resolution algorithm for the given cancellable promise. + */ +function resolverFor(promise: CancellablePromiseWithResolvers, state: CancellablePromiseState): CancellablePromiseResolver { + return (value) => { + if (state.resolving) { return; } + state.resolving = true; + + if (value === promise.promise) { + if (state.settled) { return; } + state.settled = true; + promise.reject(new TypeError("A promise cannot be resolved with itself.")); + return; + } + + if (value != null && (typeof value === 'object' || typeof value === 'function')) { + let then: any; + try { + then = (value as any).then; + } catch (err) { + state.settled = true; + promise.reject(err); + return; + } + + if (isCallable(then)) { + try { + let cancel = (value as any).cancel; + if (isCallable(cancel)) { + const oncancelled = (cause?: any) => { + Reflect.apply(cancel, value, [cause]); + }; + if (state.reason) { + // If already cancelled, propagate cancellation. + // The promise returned from the canceller algorithm does not reject + // so it can be discarded safely. + void cancellerFor({ ...promise, oncancelled }, state)(state.reason); + } else { + promise.oncancelled = oncancelled; + } + } + } catch {} + + const newState: CancellablePromiseState = { + root: state.root, + resolving: false, + get settled() { return this.root.settled }, + set settled(value) { this.root.settled = value; }, + get reason() { return this.root.reason } + }; + + const rejector = rejectorFor(promise, newState); + try { + Reflect.apply(then, value, [resolverFor(promise, newState), rejector]); + } catch (err) { + rejector(err); + } + return; // IMPORTANT! + } + } + + if (state.settled) { return; } + state.settled = true; + promise.resolve(value); + }; +} + +/** + * Returns a callback that implements the rejection algorithm for the given cancellable promise. + */ +function rejectorFor(promise: CancellablePromiseWithResolvers, state: CancellablePromiseState): CancellablePromiseRejector { + return (reason?) => { + if (state.resolving) { return; } + state.resolving = true; + + if (state.settled) { + try { + if (reason instanceof CancelError && state.reason instanceof CancelError && Object.is(reason.cause, state.reason.cause)) { + // Swallow late rejections that are CancelErrors whose cancellation cause is the same as ours. + return; + } + } catch {} + + void Promise.reject(new CancelledRejectionError(promise.promise, reason)); + } else { + state.settled = true; + promise.reject(reason); + } + } +} + +/** + * Cancels all values in an array that look like cancellable thenables. + * Returns a promise that fulfills once all cancellation procedures for the given values have settled. + */ +function cancelAll(parent: CancellablePromise, values: any[], cause?: any): Promise { + const results = []; + + for (const value of values) { + let cancel: CancellablePromiseCanceller; + try { + if (!isCallable(value.then)) { continue; } + cancel = value.cancel; + if (!isCallable(cancel)) { continue; } + } catch { continue; } + + let result: void | PromiseLike; + try { + result = Reflect.apply(cancel, value, [cause]); + } catch (err) { + Promise.reject(new CancelledRejectionError(parent, err, "Unhandled exception in cancel method.")); + continue; + } + + if (!result) { continue; } + results.push( + (result instanceof Promise ? result : Promise.resolve(result)).catch((reason?) => { + Promise.reject(new CancelledRejectionError(parent, reason, "Unhandled rejection in cancel method.")); + }) + ); + } + + return Promise.all(results) as any; +} + +/** + * Returns its argument. + */ +function identity(x: T): T { + return x; +} + +/** + * Throws its argument. + */ +function thrower(reason?: any): never { + throw reason; +} + +/** + * Attempts various strategies to convert an error to a string. + */ +function errorMessage(err: any): string { + try { + if (err instanceof Error || typeof err !== 'object' || err.toString !== Object.prototype.toString) { + return "" + err; + } + } catch {} + + try { + return JSON.stringify(err); + } catch {} + + try { + return Object.prototype.toString.call(err); + } catch {} + + return ""; +} + +/** + * Gets the current barrier promise for the given cancellable promise. If necessary, initialises the barrier. + */ +function currentBarrier(promise: CancellablePromise): Promise { + let pwr: Partial> = promise[barrierSym] ?? {}; + if (!('promise' in pwr)) { + Object.assign(pwr, promiseWithResolvers()); + } + if (promise[barrierSym] == null) { + pwr.resolve!(); + promise[barrierSym] = pwr; + } + return pwr.promise!; +} + +// Polyfill Promise.withResolvers. +let promiseWithResolvers = Promise.withResolvers; +if (promiseWithResolvers && typeof promiseWithResolvers === 'function') { + promiseWithResolvers = promiseWithResolvers.bind(Promise); +} else { + promiseWithResolvers = function (): PromiseWithResolvers { + let resolve!: (value: T | PromiseLike) => void; + let reject!: (reason?: any) => void; + const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); + return { promise, resolve, reject }; + } +} \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/clipboard.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/clipboard.ts new file mode 100644 index 000000000..a6f2f1985 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/clipboard.ts @@ -0,0 +1,35 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import {newRuntimeCaller, objectNames} from "./runtime.js"; + +const call = newRuntimeCaller(objectNames.Clipboard); + +const ClipboardSetText = 0; +const ClipboardText = 1; + +/** + * Sets the text to the Clipboard. + * + * @param text - The text to be set to the Clipboard. + * @return A Promise that resolves when the operation is successful. + */ +export function SetText(text: string): Promise { + return call(ClipboardSetText, {text}); +} + +/** + * Get the Clipboard text + * + * @returns A promise that resolves with the text from the Clipboard. + */ +export function Text(): Promise { + return call(ClipboardText); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/contextmenu.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/contextmenu.ts new file mode 100644 index 000000000..5f15bf237 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/contextmenu.ts @@ -0,0 +1,94 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { newRuntimeCaller, objectNames } from "./runtime.js"; +import { IsDebug } from "./system.js"; +import { eventTarget } from "./utils"; + +// setup +window.addEventListener('contextmenu', contextMenuHandler); + +const call = newRuntimeCaller(objectNames.ContextMenu); + +const ContextMenuOpen = 0; + +function openContextMenu(id: string, x: number, y: number, data: any): void { + void call(ContextMenuOpen, {id, x, y, data}); +} + +function contextMenuHandler(event: MouseEvent) { + const target = eventTarget(event); + + // Check for custom context menu + const customContextMenu = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu").trim(); + + if (customContextMenu) { + event.preventDefault(); + const data = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu-data"); + openContextMenu(customContextMenu, event.clientX, event.clientY, data); + } else { + processDefaultContextMenu(event, target); + } +} + + +/* +--default-contextmenu: auto; (default) will show the default context menu if contentEditable is true OR text has been selected OR element is input or textarea +--default-contextmenu: show; will always show the default context menu +--default-contextmenu: hide; will always hide the default context menu + +This rule is inherited like normal CSS rules, so nesting works as expected +*/ +function processDefaultContextMenu(event: MouseEvent, target: HTMLElement) { + // Debug builds always show the menu + if (IsDebug()) { + return; + } + + // Process default context menu + switch (window.getComputedStyle(target).getPropertyValue("--default-contextmenu").trim()) { + case 'show': + return; + case 'hide': + event.preventDefault(); + return; + } + + // Check if contentEditable is true + if (target.isContentEditable) { + return; + } + + // Check if text has been selected + const selection = window.getSelection(); + const hasSelection = selection && selection.toString().length > 0; + if (hasSelection) { + for (let i = 0; i < selection.rangeCount; i++) { + const range = selection.getRangeAt(i); + const rects = range.getClientRects(); + for (let j = 0; j < rects.length; j++) { + const rect = rects[j]; + if (document.elementFromPoint(rect.left, rect.top) === target) { + return; + } + } + } + } + + // Check if tag is input or textarea. + if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { + if (hasSelection || (!target.readOnly && !target.disabled)) { + return; + } + } + + // hide default context menu + event.preventDefault(); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/create.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/create.ts new file mode 100644 index 000000000..72965eaa6 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/create.ts @@ -0,0 +1,106 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/** + * Any is a dummy creation function for simple or unknown types. + */ +export function Any(source: any): T { + return source; +} + +/** + * ByteSlice is a creation function that replaces + * null strings with empty strings. + */ +export function ByteSlice(source: any): string { + return ((source == null) ? "" : source); +} + +/** + * Array takes a creation function for an arbitrary type + * and returns an in-place creation function for an array + * whose elements are of that type. + */ +export function Array(element: (source: any) => T): (source: any) => T[] { + if (element === Any) { + return (source) => (source === null ? [] : source); + } + + return (source) => { + if (source === null) { + return []; + } + for (let i = 0; i < source.length; i++) { + source[i] = element(source[i]); + } + return source; + }; +} + +/** + * Map takes creation functions for two arbitrary types + * and returns an in-place creation function for an object + * whose keys and values are of those types. + */ +export function Map(key: (source: any) => string, value: (source: any) => V): (source: any) => Record { + if (value === Any) { + return (source) => (source === null ? {} : source); + } + + return (source) => { + if (source === null) { + return {}; + } + for (const key in source) { + source[key] = value(source[key]); + } + return source; + }; +} + +/** + * Nullable takes a creation function for an arbitrary type + * and returns a creation function for a nullable value of that type. + */ +export function Nullable(element: (source: any) => T): (source: any) => (T | null) { + if (element === Any) { + return Any; + } + + return (source) => (source === null ? null : element(source)); +} + +/** + * Struct takes an object mapping field names to creation functions + * and returns an in-place creation function for a struct. + */ +export function Struct(createField: Record any>): + = any>(source: any) => U +{ + let allAny = true; + for (const name in createField) { + if (createField[name] !== Any) { + allAny = false; + break; + } + } + if (allAny) { + return Any; + } + + return (source) => { + for (const name in createField) { + if (name in source) { + source[name] = createField[name](source[name]); + } + } + return source; + }; +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.ts new file mode 100644 index 000000000..bb0625b2e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.ts @@ -0,0 +1,255 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import {newRuntimeCaller, objectNames} from "./runtime.js"; +import { nanoid } from './nanoid.js'; + +// setup +window._wails = window._wails || {}; +window._wails.dialogErrorCallback = dialogErrorCallback; +window._wails.dialogResultCallback = dialogResultCallback; + +type PromiseResolvers = Omit, "promise">; + +const call = newRuntimeCaller(objectNames.Dialog); +const dialogResponses = new Map(); + +// Define constants from the `methods` object in Title Case +const DialogInfo = 0; +const DialogWarning = 1; +const DialogError = 2; +const DialogQuestion = 3; +const DialogOpenFile = 4; +const DialogSaveFile = 5; + +export interface OpenFileDialogOptions { + /** Indicates if directories can be chosen. */ + CanChooseDirectories?: boolean; + /** Indicates if files can be chosen. */ + CanChooseFiles?: boolean; + /** Indicates if directories can be created. */ + CanCreateDirectories?: boolean; + /** Indicates if hidden files should be shown. */ + ShowHiddenFiles?: boolean; + /** Indicates if aliases should be resolved. */ + ResolvesAliases?: boolean; + /** Indicates if multiple selection is allowed. */ + AllowsMultipleSelection?: boolean; + /** Indicates if the extension should be hidden. */ + HideExtension?: boolean; + /** Indicates if hidden extensions can be selected. */ + CanSelectHiddenExtension?: boolean; + /** Indicates if file packages should be treated as directories. */ + TreatsFilePackagesAsDirectories?: boolean; + /** Indicates if other file types are allowed. */ + AllowsOtherFiletypes?: boolean; + /** Array of file filters. */ + Filters?: FileFilter[]; + /** Title of the dialog. */ + Title?: string; + /** Message to show in the dialog. */ + Message?: string; + /** Text to display on the button. */ + ButtonText?: string; + /** Directory to open in the dialog. */ + Directory?: string; + /** Indicates if the dialog should appear detached from the main window. */ + Detached?: boolean; +} + +export interface SaveFileDialogOptions { + /** Default filename to use in the dialog. */ + Filename?: string; + /** Indicates if directories can be chosen. */ + CanChooseDirectories?: boolean; + /** Indicates if files can be chosen. */ + CanChooseFiles?: boolean; + /** Indicates if directories can be created. */ + CanCreateDirectories?: boolean; + /** Indicates if hidden files should be shown. */ + ShowHiddenFiles?: boolean; + /** Indicates if aliases should be resolved. */ + ResolvesAliases?: boolean; + /** Indicates if the extension should be hidden. */ + HideExtension?: boolean; + /** Indicates if hidden extensions can be selected. */ + CanSelectHiddenExtension?: boolean; + /** Indicates if file packages should be treated as directories. */ + TreatsFilePackagesAsDirectories?: boolean; + /** Indicates if other file types are allowed. */ + AllowsOtherFiletypes?: boolean; + /** Array of file filters. */ + Filters?: FileFilter[]; + /** Title of the dialog. */ + Title?: string; + /** Message to show in the dialog. */ + Message?: string; + /** Text to display on the button. */ + ButtonText?: string; + /** Directory to open in the dialog. */ + Directory?: string; + /** Indicates if the dialog should appear detached from the main window. */ + Detached?: boolean; +} + +export interface MessageDialogOptions { + /** The title of the dialog window. */ + Title?: string; + /** The main message to show in the dialog. */ + Message?: string; + /** Array of button options to show in the dialog. */ + Buttons?: Button[]; + /** True if the dialog should appear detached from the main window (if applicable). */ + Detached?: boolean; +} + +export interface Button { + /** Text that appears within the button. */ + Label?: string; + /** True if the button should cancel an operation when clicked. */ + IsCancel?: boolean; + /** True if the button should be the default action when the user presses enter. */ + IsDefault?: boolean; +} + +export interface FileFilter { + /** Display name for the filter, it could be "Text Files", "Images" etc. */ + DisplayName?: string; + /** Pattern to match for the filter, e.g. "*.txt;*.md" for text markdown files. */ + Pattern?: string; +} + +/** + * Handles the result of a dialog request. + * + * @param id - The id of the request to handle the result for. + * @param data - The result data of the request. + * @param isJSON - Indicates whether the data is JSON or not. + */ +function dialogResultCallback(id: string, data: string, isJSON: boolean): void { + let resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + + if (isJSON) { + try { + resolvers.resolve(JSON.parse(data)); + } catch (err: any) { + resolvers.reject(new TypeError("could not parse result: " + err.message, { cause: err })); + } + } else { + resolvers.resolve(data); + } +} + +/** + * Handles the error from a dialog request. + * + * @param id - The id of the promise handler. + * @param message - An error message. + */ +function dialogErrorCallback(id: string, message: string): void { + getAndDeleteResponse(id)?.reject(new window.Error(message)); +} + +/** + * Retrieves and removes the response associated with the given ID from the dialogResponses map. + * + * @param id - The ID of the response to be retrieved and removed. + * @returns The response object associated with the given ID, if any. + */ +function getAndDeleteResponse(id: string): PromiseResolvers | undefined { + const response = dialogResponses.get(id); + dialogResponses.delete(id); + return response; +} + +/** + * Generates a unique ID using the nanoid library. + * + * @returns A unique ID that does not exist in the dialogResponses set. + */ +function generateID(): string { + let result; + do { + result = nanoid(); + } while (dialogResponses.has(result)); + return result; +} + +/** + * Presents a dialog of specified type with the given options. + * + * @param type - Dialog type. + * @param options - Options for the dialog. + * @returns A promise that resolves with result of dialog. + */ +function dialog(type: number, options: MessageDialogOptions | OpenFileDialogOptions | SaveFileDialogOptions = {}): Promise { + const id = generateID(); + return new Promise((resolve, reject) => { + dialogResponses.set(id, { resolve, reject }); + call(type, Object.assign({ "dialog-id": id }, options)).catch((err: any) => { + dialogResponses.delete(id); + reject(err); + }); + }); +} + +/** + * Presents an info dialog. + * + * @param options - Dialog options + * @returns A promise that resolves with the label of the chosen button. + */ +export function Info(options: MessageDialogOptions): Promise { return dialog(DialogInfo, options); } + +/** + * Presents a warning dialog. + * + * @param options - Dialog options. + * @returns A promise that resolves with the label of the chosen button. + */ +export function Warning(options: MessageDialogOptions): Promise { return dialog(DialogWarning, options); } + +/** + * Presents an error dialog. + * + * @param options - Dialog options. + * @returns A promise that resolves with the label of the chosen button. + */ +export function Error(options: MessageDialogOptions): Promise { return dialog(DialogError, options); } + +/** + * Presents a question dialog. + * + * @param options - Dialog options. + * @returns A promise that resolves with the label of the chosen button. + */ +export function Question(options: MessageDialogOptions): Promise { return dialog(DialogQuestion, options); } + +/** + * Presents a file selection dialog to pick one or more files to open. + * + * @param options - Dialog options. + * @returns Selected file or list of files, or a blank string/empty list if no file has been selected. + */ +export function OpenFile(options: OpenFileDialogOptions & { AllowsMultipleSelection: true }): Promise; +export function OpenFile(options: OpenFileDialogOptions & { AllowsMultipleSelection?: false | undefined }): Promise; +export function OpenFile(options: OpenFileDialogOptions): Promise; +export function OpenFile(options: OpenFileDialogOptions): Promise { return dialog(DialogOpenFile, options) ?? []; } + +/** + * Presents a file selection dialog to pick a file to save. + * + * @param options - Dialog options. + * @returns Selected file, or a blank string if no file has been selected. + */ +export function SaveFile(options: SaveFileDialogOptions): Promise { return dialog(DialogSaveFile, options); } diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts new file mode 100644 index 000000000..919c03c2c --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts @@ -0,0 +1,237 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { invoke, IsWindows } from "./system.js"; +import { GetFlag } from "./flags.js"; +import { canTrackButtons, eventTarget } from "./utils.js"; + +// Setup +let canDrag = false; +let dragging = false; + +let resizable = false; +let canResize = false; +let resizing = false; +let resizeEdge: string = ""; +let defaultCursor = "auto"; + +let buttons = 0; +const buttonsTracked = canTrackButtons(); + +window._wails = window._wails || {}; +window._wails.setResizable = (value: boolean): void => { + resizable = value; + if (!resizable) { + // Stop resizing if in progress. + canResize = resizing = false; + setResize(); + } +}; + +window.addEventListener('mousedown', update, { capture: true }); +window.addEventListener('mousemove', update, { capture: true }); +window.addEventListener('mouseup', update, { capture: true }); +for (const ev of ['click', 'contextmenu', 'dblclick']) { + window.addEventListener(ev, suppressEvent, { capture: true }); +} + +function suppressEvent(event: Event) { + // Suppress click events while resizing or dragging. + if (dragging || resizing) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } +} + +// Use constants to avoid comparing strings multiple times. +const MouseDown = 0; +const MouseUp = 1; +const MouseMove = 2; + +function update(event: MouseEvent) { + // Windows suppresses mouse events at the end of dragging or resizing, + // so we need to be smart and synthesize button events. + + let eventType: number, eventButtons = event.buttons; + switch (event.type) { + case 'mousedown': + eventType = MouseDown; + if (!buttonsTracked) { eventButtons = buttons | (1 << event.button); } + break; + case 'mouseup': + eventType = MouseUp; + if (!buttonsTracked) { eventButtons = buttons & ~(1 << event.button); } + break; + default: + eventType = MouseMove; + if (!buttonsTracked) { eventButtons = buttons; } + break; + } + + let released = buttons & ~eventButtons; + let pressed = eventButtons & ~buttons; + + buttons = eventButtons; + + // Synthesize a release-press sequence if we detect a press of an already pressed button. + if (eventType === MouseDown && !(pressed & event.button)) { + released |= (1 << event.button); + pressed |= (1 << event.button); + } + + // Suppress all button events during dragging and resizing, + // unless this is a mouseup event that is ending a drag action. + if ( + eventType !== MouseMove // Fast path for mousemove + && resizing + || ( + dragging + && ( + eventType === MouseDown + || event.button !== 0 + ) + ) + ) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } + + // Handle releases + if (released & 1) { primaryUp(event); } + // Handle presses + if (pressed & 1) { primaryDown(event); } + + // Handle mousemove + if (eventType === MouseMove) { onMouseMove(event); }; +} + +function primaryDown(event: MouseEvent): void { + // Reset readiness state. + canDrag = false; + canResize = false; + + // Ignore repeated clicks on macOS and Linux. + if (!IsWindows()) { + if (event.type === 'mousedown' && event.button === 0 && event.detail !== 1) { + return; + } + } + + if (resizeEdge) { + // Ready to resize if the primary button was pressed for the first time. + canResize = true; + // Do not start drag operations when on resize edges. + return; + } + + // Retrieve target element + const target = eventTarget(event); + + // Ready to drag if the primary button was pressed for the first time on a draggable element. + // Ignore clicks on the scrollbar. + const style = window.getComputedStyle(target); + canDrag = ( + style.getPropertyValue("--wails-draggable").trim() === "drag" + && ( + event.offsetX - parseFloat(style.paddingLeft) < target.clientWidth + && event.offsetY - parseFloat(style.paddingTop) < target.clientHeight + ) + ); +} + +function primaryUp(event: MouseEvent) { + // Stop dragging and resizing. + canDrag = false; + dragging = false; + canResize = false; + resizing = false; +} + +const cursorForEdge = Object.freeze({ + "se-resize": "nwse-resize", + "sw-resize": "nesw-resize", + "nw-resize": "nwse-resize", + "ne-resize": "nesw-resize", + "w-resize": "ew-resize", + "n-resize": "ns-resize", + "s-resize": "ns-resize", + "e-resize": "ew-resize", +}) + +function setResize(edge?: keyof typeof cursorForEdge): void { + if (edge) { + if (!resizeEdge) { defaultCursor = document.body.style.cursor; } + document.body.style.cursor = cursorForEdge[edge]; + } else if (!edge && resizeEdge) { + document.body.style.cursor = defaultCursor; + } + + resizeEdge = edge || ""; +} + +function onMouseMove(event: MouseEvent): void { + if (canResize && resizeEdge) { + // Start resizing. + resizing = true; + invoke("wails:resize:" + resizeEdge); + } else if (canDrag) { + // Start dragging. + dragging = true; + invoke("wails:drag"); + } + + if (dragging || resizing) { + // Either drag or resize is ongoing, + // reset readiness and stop processing. + canDrag = canResize = false; + return; + } + + if (!resizable || !IsWindows()) { + if (resizeEdge) { setResize(); } + return; + } + + const resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5; + const resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5; + + // Extra pixels for the corner areas. + const cornerExtra = GetFlag("resizeCornerExtra") || 10; + + const rightBorder = (window.outerWidth - event.clientX) < resizeHandleWidth; + const leftBorder = event.clientX < resizeHandleWidth; + const topBorder = event.clientY < resizeHandleHeight; + const bottomBorder = (window.outerHeight - event.clientY) < resizeHandleHeight; + + // Adjust for corner areas. + const rightCorner = (window.outerWidth - event.clientX) < (resizeHandleWidth + cornerExtra); + const leftCorner = event.clientX < (resizeHandleWidth + cornerExtra); + const topCorner = event.clientY < (resizeHandleHeight + cornerExtra); + const bottomCorner = (window.outerHeight - event.clientY) < (resizeHandleHeight + cornerExtra); + + if (!leftCorner && !topCorner && !bottomCorner && !rightCorner) { + // Optimisation: out of all corner areas implies out of borders. + setResize(); + } + // Detect corners. + else if (rightCorner && bottomCorner) setResize("se-resize"); + else if (leftCorner && bottomCorner) setResize("sw-resize"); + else if (leftCorner && topCorner) setResize("nw-resize"); + else if (topCorner && rightCorner) setResize("ne-resize"); + // Detect borders. + else if (leftBorder) setResize("w-resize"); + else if (topBorder) setResize("n-resize"); + else if (bottomBorder) setResize("s-resize"); + else if (rightBorder) setResize("e-resize"); + // Out of border area. + else setResize(); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts new file mode 100644 index 000000000..64be98d2e --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts @@ -0,0 +1,234 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export const Types = Object.freeze({ + Windows: Object.freeze({ + APMPowerSettingChange: "windows:APMPowerSettingChange", + APMPowerStatusChange: "windows:APMPowerStatusChange", + APMResumeAutomatic: "windows:APMResumeAutomatic", + APMResumeSuspend: "windows:APMResumeSuspend", + APMSuspend: "windows:APMSuspend", + ApplicationStarted: "windows:ApplicationStarted", + SystemThemeChanged: "windows:SystemThemeChanged", + WebViewNavigationCompleted: "windows:WebViewNavigationCompleted", + WindowActive: "windows:WindowActive", + WindowBackgroundErase: "windows:WindowBackgroundErase", + WindowClickActive: "windows:WindowClickActive", + WindowClosing: "windows:WindowClosing", + WindowDidMove: "windows:WindowDidMove", + WindowDidResize: "windows:WindowDidResize", + WindowDPIChanged: "windows:WindowDPIChanged", + WindowDragDrop: "windows:WindowDragDrop", + WindowDragEnter: "windows:WindowDragEnter", + WindowDragLeave: "windows:WindowDragLeave", + WindowDragOver: "windows:WindowDragOver", + WindowEndMove: "windows:WindowEndMove", + WindowEndResize: "windows:WindowEndResize", + WindowFullscreen: "windows:WindowFullscreen", + WindowHide: "windows:WindowHide", + WindowInactive: "windows:WindowInactive", + WindowKeyDown: "windows:WindowKeyDown", + WindowKeyUp: "windows:WindowKeyUp", + WindowKillFocus: "windows:WindowKillFocus", + WindowNonClientHit: "windows:WindowNonClientHit", + WindowNonClientMouseDown: "windows:WindowNonClientMouseDown", + WindowNonClientMouseLeave: "windows:WindowNonClientMouseLeave", + WindowNonClientMouseMove: "windows:WindowNonClientMouseMove", + WindowNonClientMouseUp: "windows:WindowNonClientMouseUp", + WindowPaint: "windows:WindowPaint", + WindowRestore: "windows:WindowRestore", + WindowSetFocus: "windows:WindowSetFocus", + WindowShow: "windows:WindowShow", + WindowStartMove: "windows:WindowStartMove", + WindowStartResize: "windows:WindowStartResize", + WindowUnFullscreen: "windows:WindowUnFullscreen", + WindowZOrderChanged: "windows:WindowZOrderChanged", + WindowMinimise: "windows:WindowMinimise", + WindowUnMinimise: "windows:WindowUnMinimise", + WindowMaximise: "windows:WindowMaximise", + WindowUnMaximise: "windows:WindowUnMaximise", + }), + Mac: Object.freeze({ + ApplicationDidBecomeActive: "mac:ApplicationDidBecomeActive", + ApplicationDidChangeBackingProperties: "mac:ApplicationDidChangeBackingProperties", + ApplicationDidChangeEffectiveAppearance: "mac:ApplicationDidChangeEffectiveAppearance", + ApplicationDidChangeIcon: "mac:ApplicationDidChangeIcon", + ApplicationDidChangeOcclusionState: "mac:ApplicationDidChangeOcclusionState", + ApplicationDidChangeScreenParameters: "mac:ApplicationDidChangeScreenParameters", + ApplicationDidChangeStatusBarFrame: "mac:ApplicationDidChangeStatusBarFrame", + ApplicationDidChangeStatusBarOrientation: "mac:ApplicationDidChangeStatusBarOrientation", + ApplicationDidChangeTheme: "mac:ApplicationDidChangeTheme", + ApplicationDidFinishLaunching: "mac:ApplicationDidFinishLaunching", + ApplicationDidHide: "mac:ApplicationDidHide", + ApplicationDidResignActive: "mac:ApplicationDidResignActive", + ApplicationDidUnhide: "mac:ApplicationDidUnhide", + ApplicationDidUpdate: "mac:ApplicationDidUpdate", + ApplicationShouldHandleReopen: "mac:ApplicationShouldHandleReopen", + ApplicationWillBecomeActive: "mac:ApplicationWillBecomeActive", + ApplicationWillFinishLaunching: "mac:ApplicationWillFinishLaunching", + ApplicationWillHide: "mac:ApplicationWillHide", + ApplicationWillResignActive: "mac:ApplicationWillResignActive", + ApplicationWillTerminate: "mac:ApplicationWillTerminate", + ApplicationWillUnhide: "mac:ApplicationWillUnhide", + ApplicationWillUpdate: "mac:ApplicationWillUpdate", + MenuDidAddItem: "mac:MenuDidAddItem", + MenuDidBeginTracking: "mac:MenuDidBeginTracking", + MenuDidClose: "mac:MenuDidClose", + MenuDidDisplayItem: "mac:MenuDidDisplayItem", + MenuDidEndTracking: "mac:MenuDidEndTracking", + MenuDidHighlightItem: "mac:MenuDidHighlightItem", + MenuDidOpen: "mac:MenuDidOpen", + MenuDidPopUp: "mac:MenuDidPopUp", + MenuDidRemoveItem: "mac:MenuDidRemoveItem", + MenuDidSendAction: "mac:MenuDidSendAction", + MenuDidSendActionToItem: "mac:MenuDidSendActionToItem", + MenuDidUpdate: "mac:MenuDidUpdate", + MenuWillAddItem: "mac:MenuWillAddItem", + MenuWillBeginTracking: "mac:MenuWillBeginTracking", + MenuWillDisplayItem: "mac:MenuWillDisplayItem", + MenuWillEndTracking: "mac:MenuWillEndTracking", + MenuWillHighlightItem: "mac:MenuWillHighlightItem", + MenuWillOpen: "mac:MenuWillOpen", + MenuWillPopUp: "mac:MenuWillPopUp", + MenuWillRemoveItem: "mac:MenuWillRemoveItem", + MenuWillSendAction: "mac:MenuWillSendAction", + MenuWillSendActionToItem: "mac:MenuWillSendActionToItem", + MenuWillUpdate: "mac:MenuWillUpdate", + WebViewDidCommitNavigation: "mac:WebViewDidCommitNavigation", + WebViewDidFinishNavigation: "mac:WebViewDidFinishNavigation", + WebViewDidReceiveServerRedirectForProvisionalNavigation: "mac:WebViewDidReceiveServerRedirectForProvisionalNavigation", + WebViewDidStartProvisionalNavigation: "mac:WebViewDidStartProvisionalNavigation", + WindowDidBecomeKey: "mac:WindowDidBecomeKey", + WindowDidBecomeMain: "mac:WindowDidBecomeMain", + WindowDidBeginSheet: "mac:WindowDidBeginSheet", + WindowDidChangeAlpha: "mac:WindowDidChangeAlpha", + WindowDidChangeBackingLocation: "mac:WindowDidChangeBackingLocation", + WindowDidChangeBackingProperties: "mac:WindowDidChangeBackingProperties", + WindowDidChangeCollectionBehavior: "mac:WindowDidChangeCollectionBehavior", + WindowDidChangeEffectiveAppearance: "mac:WindowDidChangeEffectiveAppearance", + WindowDidChangeOcclusionState: "mac:WindowDidChangeOcclusionState", + WindowDidChangeOrderingMode: "mac:WindowDidChangeOrderingMode", + WindowDidChangeScreen: "mac:WindowDidChangeScreen", + WindowDidChangeScreenParameters: "mac:WindowDidChangeScreenParameters", + WindowDidChangeScreenProfile: "mac:WindowDidChangeScreenProfile", + WindowDidChangeScreenSpace: "mac:WindowDidChangeScreenSpace", + WindowDidChangeScreenSpaceProperties: "mac:WindowDidChangeScreenSpaceProperties", + WindowDidChangeSharingType: "mac:WindowDidChangeSharingType", + WindowDidChangeSpace: "mac:WindowDidChangeSpace", + WindowDidChangeSpaceOrderingMode: "mac:WindowDidChangeSpaceOrderingMode", + WindowDidChangeTitle: "mac:WindowDidChangeTitle", + WindowDidChangeToolbar: "mac:WindowDidChangeToolbar", + WindowDidDeminiaturize: "mac:WindowDidDeminiaturize", + WindowDidEndSheet: "mac:WindowDidEndSheet", + WindowDidEnterFullScreen: "mac:WindowDidEnterFullScreen", + WindowDidEnterVersionBrowser: "mac:WindowDidEnterVersionBrowser", + WindowDidExitFullScreen: "mac:WindowDidExitFullScreen", + WindowDidExitVersionBrowser: "mac:WindowDidExitVersionBrowser", + WindowDidExpose: "mac:WindowDidExpose", + WindowDidFocus: "mac:WindowDidFocus", + WindowDidMiniaturize: "mac:WindowDidMiniaturize", + WindowDidMove: "mac:WindowDidMove", + WindowDidOrderOffScreen: "mac:WindowDidOrderOffScreen", + WindowDidOrderOnScreen: "mac:WindowDidOrderOnScreen", + WindowDidResignKey: "mac:WindowDidResignKey", + WindowDidResignMain: "mac:WindowDidResignMain", + WindowDidResize: "mac:WindowDidResize", + WindowDidUpdate: "mac:WindowDidUpdate", + WindowDidUpdateAlpha: "mac:WindowDidUpdateAlpha", + WindowDidUpdateCollectionBehavior: "mac:WindowDidUpdateCollectionBehavior", + WindowDidUpdateCollectionProperties: "mac:WindowDidUpdateCollectionProperties", + WindowDidUpdateShadow: "mac:WindowDidUpdateShadow", + WindowDidUpdateTitle: "mac:WindowDidUpdateTitle", + WindowDidUpdateToolbar: "mac:WindowDidUpdateToolbar", + WindowDidZoom: "mac:WindowDidZoom", + WindowFileDraggingEntered: "mac:WindowFileDraggingEntered", + WindowFileDraggingExited: "mac:WindowFileDraggingExited", + WindowFileDraggingPerformed: "mac:WindowFileDraggingPerformed", + WindowHide: "mac:WindowHide", + WindowMaximise: "mac:WindowMaximise", + WindowUnMaximise: "mac:WindowUnMaximise", + WindowMinimise: "mac:WindowMinimise", + WindowUnMinimise: "mac:WindowUnMinimise", + WindowShouldClose: "mac:WindowShouldClose", + WindowShow: "mac:WindowShow", + WindowWillBecomeKey: "mac:WindowWillBecomeKey", + WindowWillBecomeMain: "mac:WindowWillBecomeMain", + WindowWillBeginSheet: "mac:WindowWillBeginSheet", + WindowWillChangeOrderingMode: "mac:WindowWillChangeOrderingMode", + WindowWillClose: "mac:WindowWillClose", + WindowWillDeminiaturize: "mac:WindowWillDeminiaturize", + WindowWillEnterFullScreen: "mac:WindowWillEnterFullScreen", + WindowWillEnterVersionBrowser: "mac:WindowWillEnterVersionBrowser", + WindowWillExitFullScreen: "mac:WindowWillExitFullScreen", + WindowWillExitVersionBrowser: "mac:WindowWillExitVersionBrowser", + WindowWillFocus: "mac:WindowWillFocus", + WindowWillMiniaturize: "mac:WindowWillMiniaturize", + WindowWillMove: "mac:WindowWillMove", + WindowWillOrderOffScreen: "mac:WindowWillOrderOffScreen", + WindowWillOrderOnScreen: "mac:WindowWillOrderOnScreen", + WindowWillResignMain: "mac:WindowWillResignMain", + WindowWillResize: "mac:WindowWillResize", + WindowWillUnfocus: "mac:WindowWillUnfocus", + WindowWillUpdate: "mac:WindowWillUpdate", + WindowWillUpdateAlpha: "mac:WindowWillUpdateAlpha", + WindowWillUpdateCollectionBehavior: "mac:WindowWillUpdateCollectionBehavior", + WindowWillUpdateCollectionProperties: "mac:WindowWillUpdateCollectionProperties", + WindowWillUpdateShadow: "mac:WindowWillUpdateShadow", + WindowWillUpdateTitle: "mac:WindowWillUpdateTitle", + WindowWillUpdateToolbar: "mac:WindowWillUpdateToolbar", + WindowWillUpdateVisibility: "mac:WindowWillUpdateVisibility", + WindowWillUseStandardFrame: "mac:WindowWillUseStandardFrame", + WindowZoomIn: "mac:WindowZoomIn", + WindowZoomOut: "mac:WindowZoomOut", + WindowZoomReset: "mac:WindowZoomReset", + }), + Linux: Object.freeze({ + ApplicationStartup: "linux:ApplicationStartup", + SystemThemeChanged: "linux:SystemThemeChanged", + WindowDeleteEvent: "linux:WindowDeleteEvent", + WindowDidMove: "linux:WindowDidMove", + WindowDidResize: "linux:WindowDidResize", + WindowFocusIn: "linux:WindowFocusIn", + WindowFocusOut: "linux:WindowFocusOut", + WindowLoadChanged: "linux:WindowLoadChanged", + }), + Common: Object.freeze({ + ApplicationOpenedWithFile: "common:ApplicationOpenedWithFile", + ApplicationStarted: "common:ApplicationStarted", + ApplicationLaunchedWithUrl: "common:ApplicationLaunchedWithUrl", + ThemeChanged: "common:ThemeChanged", + WindowClosing: "common:WindowClosing", + WindowDidMove: "common:WindowDidMove", + WindowDidResize: "common:WindowDidResize", + WindowDPIChanged: "common:WindowDPIChanged", + WindowFilesDropped: "common:WindowFilesDropped", + WindowFocus: "common:WindowFocus", + WindowFullscreen: "common:WindowFullscreen", + WindowHide: "common:WindowHide", + WindowLostFocus: "common:WindowLostFocus", + WindowMaximise: "common:WindowMaximise", + WindowMinimise: "common:WindowMinimise", + WindowToggleFrameless: "common:WindowToggleFrameless", + WindowRestore: "common:WindowRestore", + WindowRuntimeReady: "common:WindowRuntimeReady", + WindowShow: "common:WindowShow", + WindowUnFullscreen: "common:WindowUnFullscreen", + WindowUnMaximise: "common:WindowUnMaximise", + WindowUnMinimise: "common:WindowUnMinimise", + WindowZoom: "common:WindowZoom", + WindowZoomIn: "common:WindowZoomIn", + WindowZoomOut: "common:WindowZoomOut", + WindowZoomReset: "common:WindowZoomReset", + WindowDropZoneFilesDropped: "common:WindowDropZoneFilesDropped", + }), +}); diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/events.test.js b/v3/internal/runtime/desktop/@wailsio/runtime/src/events.test.js new file mode 100644 index 000000000..e8157a17a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/events.test.js @@ -0,0 +1,155 @@ +import { On, Off, OffAll, OnMultiple, WailsEvent, Once } from './events'; +import { eventListeners } from "./listener"; +import { expect, describe, it, vi, afterEach, beforeEach } from 'vitest'; + +const dispatchWailsEvent = window._wails.dispatchWailsEvent; + +afterEach(() => { + OffAll(); + vi.resetAllMocks(); +}); + +describe("OnMultiple", () => { + const testEvent = { name: 'a', data: ["hello", "events"] }; + const cb = vi.fn((ev) => { + expect(ev).toBeInstanceOf(WailsEvent); + expect(ev).toMatchObject(testEvent); + }); + + it("should dispatch a properly initialised WailsEvent", () => { + OnMultiple('a', cb, 5); + dispatchWailsEvent(testEvent); + expect(cb).toHaveBeenCalled(); + }); + + it("should stop after the specified number of times", () => { + OnMultiple('a', cb, 5); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + expect(cb).toHaveBeenCalledTimes(5); + }); + + it("should return a cancel fn", () => { + const cancel = OnMultiple('a', cb, 5); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + cancel(); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + expect(cb).toBeCalledTimes(2); + }); +}); + +describe("On", () => { + let testEvent = { name: 'a', data: ["hello", "events"], sender: "window" }; + const cb = vi.fn((ev) => { + expect(ev).toBeInstanceOf(WailsEvent); + expect(ev).toMatchObject(testEvent); + }); + + it("should dispatch a properly initialised WailsEvent", () => { + On('a', cb); + dispatchWailsEvent(testEvent); + expect(cb).toHaveBeenCalled(); + }); + + it("should never stop", () => { + On('a', cb); + expect(eventListeners.get('a')[0].maxCallbacks).toBe(-1); + dispatchWailsEvent(testEvent); + expect(eventListeners.get('a')[0].maxCallbacks).toBe(-1); + }); + + it("should return a cancel fn", () => { + const cancel = On('a', cb) + dispatchWailsEvent(testEvent); + cancel(); + dispatchWailsEvent(testEvent); + expect(cb).toHaveBeenCalledTimes(1); + }); +}); + +describe("Once", () => { + const testEvent = { name: 'a', data: ["hello", "events"] }; + const cb = vi.fn((ev) => { + expect(ev).toBeInstanceOf(WailsEvent); + expect(ev).toMatchObject(testEvent); + }); + + it("should dispatch a properly initialised WailsEvent", () => { + Once('a', cb); + dispatchWailsEvent(testEvent); + expect(cb).toHaveBeenCalled(); + }); + + it("should stop after one time", () => { + Once('a', cb) + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + dispatchWailsEvent(testEvent); + expect(cb).toHaveBeenCalledTimes(1); + }); + + it("should return a cancel fn", () => { + const cancel = Once('a', cb) + cancel(); + dispatchWailsEvent(testEvent); + expect(cb).not.toHaveBeenCalled(); + }); +}) + +describe("Off", () => { + const cba = vi.fn(), cbb = vi.fn(), cbc = vi.fn(); + + beforeEach(() => { + On('a', cba); + On('a', cba); + On('a', cba); + On('b', cbb); + On('c', cbc); + On('c', cbc); + }); + + it("should cancel all event listeners for a single type", () => { + Off('a'); + dispatchWailsEvent({ name: 'a' }); + dispatchWailsEvent({ name: 'b' }); + dispatchWailsEvent({ name: 'c' }); + expect(cba).not.toHaveBeenCalled(); + expect(cbb).toHaveBeenCalledTimes(1); + expect(cbc).toHaveBeenCalledTimes(2); + }); + + it("should cancel all event listeners for multiple types", () => { + Off('a', 'c') + dispatchWailsEvent({ name: 'a' }); + dispatchWailsEvent({ name: 'b' }); + dispatchWailsEvent({ name: 'c' }); + expect(cba).not.toHaveBeenCalled(); + expect(cbb).toHaveBeenCalledTimes(1); + expect(cbc).not.toHaveBeenCalled(); + }); +}); + +describe("OffAll", () => { + it("should cancel all event listeners", () => { + const cba = vi.fn(), cbb = vi.fn(), cbc = vi.fn(); + On('a', cba); + On('a', cba); + On('a', cba); + On('b', cbb); + On('c', cbc); + On('c', cbc); + OffAll(); + dispatchWailsEvent({ name: 'a' }); + dispatchWailsEvent({ name: 'b' }); + dispatchWailsEvent({ name: 'c' }); + expect(cba).not.toHaveBeenCalled(); + expect(cbb).not.toHaveBeenCalled(); + expect(cbc).not.toHaveBeenCalled(); + }); +}); diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/events.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/events.ts new file mode 100644 index 000000000..7ad7642b6 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/events.ts @@ -0,0 +1,150 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { newRuntimeCaller, objectNames } from "./runtime.js"; +import { eventListeners, Listener, listenerOff } from "./listener.js"; + +// Setup +window._wails = window._wails || {}; +window._wails.dispatchWailsEvent = dispatchWailsEvent; + +const call = newRuntimeCaller(objectNames.Events); +const EmitMethod = 0; + +export { Types } from "./event_types.js"; + +/** + * The type of handlers for a given event. + */ +export type Callback = (ev: WailsEvent) => void; + +/** + * Represents a system event or a custom event emitted through wails-provided facilities. + */ +export class WailsEvent { + /** + * The name of the event. + */ + name: string; + + /** + * Optional data associated with the emitted event. + */ + data: any; + + /** + * Name of the originating window. Omitted for application events. + * Will be overridden if set manually. + */ + sender?: string; + + constructor(name: string, data: any = null) { + this.name = name; + this.data = data; + } +} + +function dispatchWailsEvent(event: any) { + let listeners = eventListeners.get(event.name); + if (!listeners) { + return; + } + + let wailsEvent = new WailsEvent(event.name, event.data); + if ('sender' in event) { + wailsEvent.sender = event.sender; + } + + listeners = listeners.filter(listener => !listener.dispatch(wailsEvent)); + if (listeners.length === 0) { + eventListeners.delete(event.name); + } else { + eventListeners.set(event.name, listeners); + } +} + +/** + * Register a callback function to be called multiple times for a specific event. + * + * @param eventName - The name of the event to register the callback for. + * @param callback - The callback function to be called when the event is triggered. + * @param maxCallbacks - The maximum number of times the callback can be called for the event. Once the maximum number is reached, the callback will no longer be called. + * @returns A function that, when called, will unregister the callback from the event. + */ +export function OnMultiple(eventName: string, callback: Callback, maxCallbacks: number) { + let listeners = eventListeners.get(eventName) || []; + const thisListener = new Listener(eventName, callback, maxCallbacks); + listeners.push(thisListener); + eventListeners.set(eventName, listeners); + return () => listenerOff(thisListener); +} + +/** + * Registers a callback function to be executed when the specified event occurs. + * + * @param eventName - The name of the event to register the callback for. + * @param callback - The callback function to be called when the event is triggered. + * @returns A function that, when called, will unregister the callback from the event. + */ +export function On(eventName: string, callback: Callback): () => void { + return OnMultiple(eventName, callback, -1); +} + +/** + * Registers a callback function to be executed only once for the specified event. + * + * @param eventName - The name of the event to register the callback for. + * @param callback - The callback function to be called when the event is triggered. + * @returns A function that, when called, will unregister the callback from the event. + */ +export function Once(eventName: string, callback: Callback): () => void { + return OnMultiple(eventName, callback, 1); +} + +/** + * Removes event listeners for the specified event names. + * + * @param eventNames - The name of the events to remove listeners for. + */ +export function Off(...eventNames: [string, ...string[]]): void { + eventNames.forEach(eventName => eventListeners.delete(eventName)); +} + +/** + * Removes all event listeners. + */ +export function OffAll(): void { + eventListeners.clear(); +} + +/** + * Emits an event using the name and data. + * + * @returns A promise that will be fulfilled once the event has been emitted. + * @param name - the name of the event to emit. + * @param data - the data to be sent with the event. + */ +export function Emit(name: string, data?: any): Promise { + let eventName: string; + let eventData: any; + + if (typeof name === 'object' && name !== null && 'name' in name && 'data' in name) { + // If name is an object with a name property, use it directly + eventName = name['name']; + eventData = name['data']; + } else { + // Otherwise use the standard parameters + eventName = name as string; + eventData = data; + } + + return call(EmitMethod, { name: eventName, data: eventData }); +} + diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/flags.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/flags.ts new file mode 100644 index 000000000..9e4ad2427 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/flags.ts @@ -0,0 +1,23 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/** + * Retrieves the value associated with the specified key from the flag map. + * + * @param key - The key to retrieve the value for. + * @return The value associated with the specified key. + */ +export function GetFlag(key: string): any { + try { + return window._wails.flags[key]; + } catch (e) { + throw new Error("Unable to retrieve flag '" + key + "': " + e, { cause: e }); + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/global.d.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/global.d.ts new file mode 100644 index 000000000..231896ce1 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/global.d.ts @@ -0,0 +1,17 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +declare global { + interface Window { + _wails: Record; + } +} + +export {}; diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts new file mode 100644 index 000000000..9633551c2 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts @@ -0,0 +1,58 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +// Setup +window._wails = window._wails || {}; + +import "./contextmenu.js"; +import "./drag.js"; + +// Re-export public API +import * as Application from "./application.js"; +import * as Browser from "./browser.js"; +import * as Call from "./calls.js"; +import * as Clipboard from "./clipboard.js"; +import * as Create from "./create.js"; +import * as Dialogs from "./dialogs.js"; +import * as Events from "./events.js"; +import * as Flags from "./flags.js"; +import * as Screens from "./screens.js"; +import * as System from "./system.js"; +import Window from "./window.js"; +import * as WML from "./wml.js"; + +export { + Application, + Browser, + Call, + Clipboard, + Dialogs, + Events, + Flags, + Screens, + System, + Window, + WML +}; + +/** + * An internal utility consumed by the binding generator. + * + * @ignore + * @internal + * + */ +export { Create }; + +export * from "./cancellable.js"; + +// Notify backend +window._wails.invoke = System.invoke; +System.invoke("wails:runtime:ready"); diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/listener.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/listener.ts new file mode 100644 index 000000000..0d74debca --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/listener.ts @@ -0,0 +1,52 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +// The following utilities have been factored out of ./events.ts +// for testing purposes. + +export const eventListeners = new Map(); + +export class Listener { + eventName: string; + callback: (data: any) => void; + maxCallbacks: number; + + constructor(eventName: string, callback: (data: any) => void, maxCallbacks: number) { + this.eventName = eventName; + this.callback = callback; + this.maxCallbacks = maxCallbacks || -1; + } + + dispatch(data: any): boolean { + try { + this.callback(data); + } catch (err) { + console.error(err); + } + + if (this.maxCallbacks === -1) return false; + this.maxCallbacks -= 1; + return this.maxCallbacks === 0; + } +} + +export function listenerOff(listener: Listener): void { + let listeners = eventListeners.get(listener.eventName); + if (!listeners) { + return; + } + + listeners = listeners.filter(l => l !== listener); + if (listeners.length === 0) { + eventListeners.delete(listener.eventName); + } else { + eventListeners.set(listener.eventName, listeners); + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/nanoid.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/nanoid.ts new file mode 100644 index 000000000..bfe83048f --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/nanoid.ts @@ -0,0 +1,42 @@ +// Source: https://github.com/ai/nanoid + +// The MIT License (MIT) +// +// Copyright 2017 Andrey Sitnik +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// This alphabet uses `A-Za-z0-9_-` symbols. +// The order of characters is optimized for better gzip and brotli compression. +// References to the same file (works both for gzip and brotli): +// `'use`, `andom`, and `rict'` +// References to the brotli default dictionary: +// `-26T`, `1983`, `40px`, `75px`, `bush`, `jack`, `mind`, `very`, and `wolf` +const urlAlphabet = + 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict' + +export function nanoid(size: number = 21): string { + let id = '' + // A compact alternative for `for (var i = 0; i < step; i++)`. + let i = size | 0 + while (i--) { + // `| 0` is more compact and faster than `Math.floor()`. + id += urlAlphabet[(Math.random() * 64) | 0] + } + return id +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/promises_aplus.test.js b/v3/internal/runtime/desktop/@wailsio/runtime/src/promises_aplus.test.js new file mode 100644 index 000000000..baf51e3c0 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/promises_aplus.test.js @@ -0,0 +1,44 @@ +import * as util from "util"; +import * as V from "vitest"; +import { CancellablePromise } from "./cancellable"; + +// The Promises/A+ suite handles some errors late. +process.on('rejectionHandled', function () {}); + +// The Promises/A+ suite leaves some errors unhandled. +process.on('unhandledRejection', function (reason, promise) { + if (promise instanceof CancellablePromise && reason != null && typeof reason === 'object') { + for (const key of ['dummy', 'other', 'sentinel']) { + if (reason[key] === key) { + return; + } + } + } + throw new Error(`Unhandled rejection at: ${util.inspect(promise)}; reason: ${util.inspect(reason)}`, { cause: reason }); +}); + +// Emulate a minimal version of the mocha BDD API using vitest primitives. +global.context = global.describe = V.describe; +global.specify = global.it = function it(desc, fn) { + let viTestFn = fn; + if (fn?.length) { + viTestFn = () => new Promise((done) => fn(done)); + } + V.it(desc, viTestFn); +} +global.before = function(desc, fn) { V.beforeAll(typeof desc === 'function' ? desc : fn) }; +global.after = function(desc, fn) { V.afterAll(typeof desc === 'function' ? desc : fn) }; +global.beforeEach = function(desc, fn) { V.beforeEach(typeof desc === 'function' ? desc : fn) }; +global.afterEach = function(desc, fn) { V.afterEach(typeof desc === 'function' ? desc : fn) }; + +require('promises-aplus-tests').mocha({ + resolved(value) { + return CancellablePromise.resolve(value); + }, + rejected(reason) { + return CancellablePromise.reject(reason); + }, + deferred() { + return CancellablePromise.withResolvers(); + } +}); diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/runtime.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/runtime.ts new file mode 100644 index 000000000..412427ef5 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/runtime.ts @@ -0,0 +1,67 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { nanoid } from './nanoid.js'; + +const runtimeURL = window.location.origin + "/wails/runtime"; + +// Object Names +export const objectNames = Object.freeze({ + Call: 0, + Clipboard: 1, + Application: 2, + Events: 3, + ContextMenu: 4, + Dialog: 5, + Window: 6, + Screens: 7, + System: 8, + Browser: 9, + CancelCall: 10, +}); +export let clientId = nanoid(); + +/** + * Creates a new runtime caller with specified ID. + * + * @param object - The object to invoke the method on. + * @param windowName - The name of the window. + * @return The new runtime caller function. + */ +export function newRuntimeCaller(object: number, windowName: string = '') { + return function (method: number, args: any = null) { + return runtimeCallWithID(object, method, windowName, args); + }; +} + +async function runtimeCallWithID(objectID: number, method: number, windowName: string, args: any): Promise { + let url = new URL(runtimeURL); + url.searchParams.append("object", objectID.toString()); + url.searchParams.append("method", method.toString()); + if (args) { url.searchParams.append("args", JSON.stringify(args)); } + + let headers: Record = { + ["x-wails-client-id"]: clientId + } + if (windowName) { + headers["x-wails-window-name"] = windowName; + } + + let response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(await response.text()); + } + + if ((response.headers.get("Content-Type")?.indexOf("application/json") ?? -1) !== -1) { + return response.json(); + } else { + return response.text(); + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/screens.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/screens.ts new file mode 100644 index 000000000..c0ecfd7be --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/screens.ts @@ -0,0 +1,88 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +export interface Size { + /** The width of a rectangular area. */ + Width: number; + /** The height of a rectangular area. */ + Height: number; +} + +export interface Rect { + /** The X coordinate of the origin. */ + X: number; + /** The Y coordinate of the origin. */ + Y: number; + /** The width of the rectangle. */ + Width: number; + /** The height of the rectangle. */ + Height: number; +} + +export interface Screen { + /** Unique identifier for the screen. */ + ID: string; + /** Human-readable name of the screen. */ + Name: string; + /** The scale factor of the screen (DPI/96). 1 = standard DPI, 2 = HiDPI (Retina), etc. */ + ScaleFactor: number; + /** The X coordinate of the screen. */ + X: number; + /** The Y coordinate of the screen. */ + Y: number; + /** Contains the width and height of the screen. */ + Size: Size; + /** Contains the bounds of the screen in terms of X, Y, Width, and Height. */ + Bounds: Rect; + /** Contains the physical bounds of the screen in terms of X, Y, Width, and Height (before scaling). */ + PhysicalBounds: Rect; + /** Contains the area of the screen that is actually usable (excluding taskbar and other system UI). */ + WorkArea: Rect; + /** Contains the physical WorkArea of the screen (before scaling). */ + PhysicalWorkArea: Rect; + /** True if this is the primary monitor selected by the user in the operating system. */ + IsPrimary: boolean; + /** The rotation of the screen. */ + Rotation: number; +} + +import { newRuntimeCaller, objectNames } from "./runtime.js"; +const call = newRuntimeCaller(objectNames.Screens); + +const getAll = 0; +const getPrimary = 1; +const getCurrent = 2; + +/** + * Gets all screens. + * + * @returns A promise that resolves to an array of Screen objects. + */ +export function GetAll(): Promise { + return call(getAll); +} + +/** + * Gets the primary screen. + * + * @returns A promise that resolves to the primary screen. + */ +export function GetPrimary(): Promise { + return call(getPrimary); +} + +/** + * Gets the current active screen. + * + * @returns A promise that resolves with the current active screen. + */ +export function GetCurrent(): Promise { + return call(getCurrent); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/system.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/system.ts new file mode 100644 index 000000000..efa40c83b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/system.ts @@ -0,0 +1,189 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { newRuntimeCaller, objectNames } from "./runtime.js"; + +const call = newRuntimeCaller(objectNames.System); + +const SystemIsDarkMode = 0; +const SystemEnvironment = 1; +const ApplicationFilesDroppedWithContext = 100; // New method ID for enriched drop event + +const _invoke = (function () { + try { + if ((window as any).chrome?.webview?.postMessage) { + return (window as any).chrome.webview.postMessage.bind((window as any).chrome.webview); + } else if ((window as any).webkit?.messageHandlers?.['external']?.postMessage) { + return (window as any).webkit.messageHandlers['external'].postMessage.bind((window as any).webkit.messageHandlers['external']); + } + } catch(e) {} + + console.warn('\n%c⚠️ Browser Environment Detected %c\n\n%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode.\nMore information at: https://v3.wails.io/learn/build/#using-a-browser-for-development\n', + 'background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;', + 'background: transparent;', + 'color: #ffffff; font-style: italic; font-weight: bold;'); + return null; +})(); + +export function invoke(msg: any): void { + _invoke?.(msg); +} + +/** + * Retrieves the system dark mode status. + * + * @returns A promise that resolves to a boolean value indicating if the system is in dark mode. + */ +export function IsDarkMode(): Promise { + return call(SystemIsDarkMode); +} + +/** + * Fetches the capabilities of the application from the server. + * + * @returns A promise that resolves to an object containing the capabilities. + */ +export async function Capabilities(): Promise> { + let response = await fetch("/wails/capabilities"); + if (response.ok) { + return response.json(); + } else { + throw new Error("could not fetch capabilities: " + response.statusText); + } +} + +export interface OSInfo { + /** The branding of the OS. */ + Branding: string; + /** The ID of the OS. */ + ID: string; + /** The name of the OS. */ + Name: string; + /** The version of the OS. */ + Version: string; +} + +export interface EnvironmentInfo { + /** The architecture of the system. */ + Arch: string; + /** True if the application is running in debug mode, otherwise false. */ + Debug: boolean; + /** The operating system in use. */ + OS: string; + /** Details of the operating system. */ + OSInfo: OSInfo; + /** Additional platform information. */ + PlatformInfo: Record; +} + +/** + * Retrieves environment details. + * + * @returns A promise that resolves to an object containing OS and system architecture. + */ +export function Environment(): Promise { + return call(SystemEnvironment); +} + +/** + * Checks if the current operating system is Windows. + * + * @return True if the operating system is Windows, otherwise false. + */ +export function IsWindows(): boolean { + return window._wails.environment.OS === "windows"; +} + +/** + * Checks if the current operating system is Linux. + * + * @returns Returns true if the current operating system is Linux, false otherwise. + */ +export function IsLinux(): boolean { + return window._wails.environment.OS === "linux"; +} + +/** + * Checks if the current environment is a macOS operating system. + * + * @returns True if the environment is macOS, false otherwise. + */ +export function IsMac(): boolean { + return window._wails.environment.OS === "darwin"; +} + +/** + * Checks if the current environment architecture is AMD64. + * + * @returns True if the current environment architecture is AMD64, false otherwise. + */ +export function IsAMD64(): boolean { + return window._wails.environment.Arch === "amd64"; +} + +/** + * Checks if the current architecture is ARM. + * + * @returns True if the current architecture is ARM, false otherwise. + */ +export function IsARM(): boolean { + return window._wails.environment.Arch === "arm"; +} + +/** + * Checks if the current environment is ARM64 architecture. + * + * @returns Returns true if the environment is ARM64 architecture, otherwise returns false. + */ +export function IsARM64(): boolean { + return window._wails.environment.Arch === "arm64"; +} + +/** + * Reports whether the app is being run in debug mode. + * + * @returns True if the app is being run in debug mode. + */ +export function IsDebug(): boolean { + return Boolean(window._wails.environment.Debug); +} + +/** + * Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop). + * Gathers information about the drop target element and sends it back to the Go backend. + * + * @param filenames - An array of file paths (strings) that were dropped. + * @param x - The x-coordinate of the drop event. + * @param y - The y-coordinate of the drop event. + */ +export function HandlePlatformFileDrop(filenames: string[], x: number, y: number): void { + const element = document.elementFromPoint(x, y); + const elementId = element ? element.id : ''; + const classList = element ? Array.from(element.classList) : []; + + const payload = { + filenames, + x, + y, + elementId, + classList, + }; + + call(ApplicationFilesDroppedWithContext, payload) + .then(() => { + // Optional: Log success or handle if needed + console.log("Platform file drop processed and sent to Go."); + }) + .catch(err => { + // Optional: Log error + console.error("Error sending platform file drop to Go:", err); + }); +} + diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/utils.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/utils.ts new file mode 100644 index 000000000..35b09463b --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/utils.ts @@ -0,0 +1,105 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/** + * Logs a message to the console with custom formatting. + * + * @param message - The message to be logged. + */ +export function debugLog(message: any) { + // eslint-disable-next-line + console.log( + '%c wails3 %c ' + message + ' ', + 'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem', + 'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem' + ); +} + +/** + * Checks whether the webview supports the {@link MouseEvent#buttons} property. + * Looking at you macOS High Sierra! + */ +export function canTrackButtons(): boolean { + return (new MouseEvent('mousedown')).buttons === 0; +} + +/** + * Checks whether the browser supports removing listeners by triggering an AbortSignal + * (see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal). + */ +export function canAbortListeners() { + if (!EventTarget || !AbortSignal || !AbortController) + return false; + + let result = true; + + const target = new EventTarget(); + const controller = new AbortController(); + target.addEventListener('test', () => { result = false; }, { signal: controller.signal }); + controller.abort(); + target.dispatchEvent(new CustomEvent('test')); + + return result; +} + +/** + * Resolves the closest HTMLElement ancestor of an event's target. + */ +export function eventTarget(event: Event): HTMLElement { + if (event.target instanceof HTMLElement) { + return event.target; + } else if (!(event.target instanceof HTMLElement) && event.target instanceof Node) { + return event.target.parentElement ?? document.body; + } else { + return document.body; + } +} + +/*** + This technique for proper load detection is taken from HTMX: + + BSD 2-Clause License + + Copyright (c) 2020, Big Sky Software + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ***/ + +let isReady = false; +document.addEventListener('DOMContentLoaded', () => { isReady = true }); + +export function whenReady(callback: () => void) { + if (isReady || document.readyState === 'complete') { + callback(); + } else { + document.addEventListener('DOMContentLoaded', callback); + } +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts new file mode 100644 index 000000000..3a2519d1a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts @@ -0,0 +1,668 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import {newRuntimeCaller, objectNames} from "./runtime.js"; +import type { Screen } from "./screens.js"; + +// NEW: Dropzone constants +const DROPZONE_ATTRIBUTE = 'data-wails-dropzone'; +const DROPZONE_HOVER_CLASS = 'wails-dropzone-hover'; // User can style this class +let currentHoveredDropzone: Element | null = null; + +const PositionMethod = 0; +const CenterMethod = 1; +const CloseMethod = 2; +const DisableSizeConstraintsMethod = 3; +const EnableSizeConstraintsMethod = 4; +const FocusMethod = 5; +const ForceReloadMethod = 6; +const FullscreenMethod = 7; +const GetScreenMethod = 8; +const GetZoomMethod = 9; +const HeightMethod = 10; +const HideMethod = 11; +const IsFocusedMethod = 12; +const IsFullscreenMethod = 13; +const IsMaximisedMethod = 14; +const IsMinimisedMethod = 15; +const MaximiseMethod = 16; +const MinimiseMethod = 17; +const NameMethod = 18; +const OpenDevToolsMethod = 19; +const RelativePositionMethod = 20; +const ReloadMethod = 21; +const ResizableMethod = 22; +const RestoreMethod = 23; +const SetPositionMethod = 24; +const SetAlwaysOnTopMethod = 25; +const SetBackgroundColourMethod = 26; +const SetFramelessMethod = 27; +const SetFullscreenButtonEnabledMethod = 28; +const SetMaxSizeMethod = 29; +const SetMinSizeMethod = 30; +const SetRelativePositionMethod = 31; +const SetResizableMethod = 32; +const SetSizeMethod = 33; +const SetTitleMethod = 34; +const SetZoomMethod = 35; +const ShowMethod = 36; +const SizeMethod = 37; +const ToggleFullscreenMethod = 38; +const ToggleMaximiseMethod = 39; +const ToggleFramelessMethod = 40; +const UnFullscreenMethod = 41; +const UnMaximiseMethod = 42; +const UnMinimiseMethod = 43; +const WidthMethod = 44; +const ZoomMethod = 45; +const ZoomInMethod = 46; +const ZoomOutMethod = 47; +const ZoomResetMethod = 48; +const SnapAssistMethod = 49; +const WindowDropZoneDropped = 50; + +function getDropzoneElement(element: Element | null): Element | null { + if (!element) { + return null; + } + // Allow dropzone attribute to be on the element itself or any parent + return element.closest(`[${DROPZONE_ATTRIBUTE}]`); +} + +/** + * A record describing the position of a window. + */ +interface Position { + /** The horizontal position of the window. */ + x: number; + /** The vertical position of the window. */ + y: number; +} + +/** + * A record describing the size of a window. + */ +interface Size { + /** The width of the window. */ + width: number; + /** The height of the window. */ + height: number; +} + +// Private field names. +const callerSym = Symbol("caller"); + +class Window { + // Private fields. + private [callerSym]: (message: number, args?: any) => Promise; + + /** + * Initialises a window object with the specified name. + * + * @private + * @param name - The name of the target window. + */ + constructor(name: string = '') { + this[callerSym] = newRuntimeCaller(objectNames.Window, name) + + // bind instance method to make them easily usable in event handlers + for (const method of Object.getOwnPropertyNames(Window.prototype)) { + if ( + method !== "constructor" + && typeof (this as any)[method] === "function" + ) { + (this as any)[method] = (this as any)[method].bind(this); + } + } + } + + /** + * Gets the specified window. + * + * @param name - The name of the window to get. + * @returns The corresponding window object. + */ + Get(name: string): Window { + return new Window(name); + } + + /** + * Returns the absolute position of the window. + * + * @returns The current absolute position of the window. + */ + Position(): Promise { + return this[callerSym](PositionMethod); + } + + /** + * Centers the window on the screen. + */ + Center(): Promise { + return this[callerSym](CenterMethod); + } + + /** + * Closes the window. + */ + Close(): Promise { + return this[callerSym](CloseMethod); + } + + /** + * Disables min/max size constraints. + */ + DisableSizeConstraints(): Promise { + return this[callerSym](DisableSizeConstraintsMethod); + } + + /** + * Enables min/max size constraints. + */ + EnableSizeConstraints(): Promise { + return this[callerSym](EnableSizeConstraintsMethod); + } + + /** + * Focuses the window. + */ + Focus(): Promise { + return this[callerSym](FocusMethod); + } + + /** + * Forces the window to reload the page assets. + */ + ForceReload(): Promise { + return this[callerSym](ForceReloadMethod); + } + + /** + * Switches the window to fullscreen mode. + */ + Fullscreen(): Promise { + return this[callerSym](FullscreenMethod); + } + + /** + * Returns the screen that the window is on. + * + * @returns The screen the window is currently on. + */ + GetScreen(): Promise { + return this[callerSym](GetScreenMethod); + } + + /** + * Returns the current zoom level of the window. + * + * @returns The current zoom level. + */ + GetZoom(): Promise { + return this[callerSym](GetZoomMethod); + } + + /** + * Returns the height of the window. + * + * @returns The current height of the window. + */ + Height(): Promise { + return this[callerSym](HeightMethod); + } + + /** + * Hides the window. + */ + Hide(): Promise { + return this[callerSym](HideMethod); + } + + /** + * Returns true if the window is focused. + * + * @returns Whether the window is currently focused. + */ + IsFocused(): Promise { + return this[callerSym](IsFocusedMethod); + } + + /** + * Returns true if the window is fullscreen. + * + * @returns Whether the window is currently fullscreen. + */ + IsFullscreen(): Promise { + return this[callerSym](IsFullscreenMethod); + } + + /** + * Returns true if the window is maximised. + * + * @returns Whether the window is currently maximised. + */ + IsMaximised(): Promise { + return this[callerSym](IsMaximisedMethod); + } + + /** + * Returns true if the window is minimised. + * + * @returns Whether the window is currently minimised. + */ + IsMinimised(): Promise { + return this[callerSym](IsMinimisedMethod); + } + + /** + * Maximises the window. + */ + Maximise(): Promise { + return this[callerSym](MaximiseMethod); + } + + /** + * Minimises the window. + */ + Minimise(): Promise { + return this[callerSym](MinimiseMethod); + } + + /** + * Returns the name of the window. + * + * @returns The name of the window. + */ + Name(): Promise { + return this[callerSym](NameMethod); + } + + /** + * Opens the development tools pane. + */ + OpenDevTools(): Promise { + return this[callerSym](OpenDevToolsMethod); + } + + /** + * Returns the relative position of the window to the screen. + * + * @returns The current relative position of the window. + */ + RelativePosition(): Promise { + return this[callerSym](RelativePositionMethod); + } + + /** + * Reloads the page assets. + */ + Reload(): Promise { + return this[callerSym](ReloadMethod); + } + + /** + * Returns true if the window is resizable. + * + * @returns Whether the window is currently resizable. + */ + Resizable(): Promise { + return this[callerSym](ResizableMethod); + } + + /** + * Restores the window to its previous state if it was previously minimised, maximised or fullscreen. + */ + Restore(): Promise { + return this[callerSym](RestoreMethod); + } + + /** + * Sets the absolute position of the window. + * + * @param x - The desired horizontal absolute position of the window. + * @param y - The desired vertical absolute position of the window. + */ + SetPosition(x: number, y: number): Promise { + return this[callerSym](SetPositionMethod, { x, y }); + } + + /** + * Sets the window to be always on top. + * + * @param alwaysOnTop - Whether the window should stay on top. + */ + SetAlwaysOnTop(alwaysOnTop: boolean): Promise { + return this[callerSym](SetAlwaysOnTopMethod, { alwaysOnTop }); + } + + /** + * Sets the background colour of the window. + * + * @param r - The desired red component of the window background. + * @param g - The desired green component of the window background. + * @param b - The desired blue component of the window background. + * @param a - The desired alpha component of the window background. + */ + SetBackgroundColour(r: number, g: number, b: number, a: number): Promise { + return this[callerSym](SetBackgroundColourMethod, { r, g, b, a }); + } + + /** + * Removes the window frame and title bar. + * + * @param frameless - Whether the window should be frameless. + */ + SetFrameless(frameless: boolean): Promise { + return this[callerSym](SetFramelessMethod, { frameless }); + } + + /** + * Disables the system fullscreen button. + * + * @param enabled - Whether the fullscreen button should be enabled. + */ + SetFullscreenButtonEnabled(enabled: boolean): Promise { + return this[callerSym](SetFullscreenButtonEnabledMethod, { enabled }); + } + + /** + * Sets the maximum size of the window. + * + * @param width - The desired maximum width of the window. + * @param height - The desired maximum height of the window. + */ + SetMaxSize(width: number, height: number): Promise { + return this[callerSym](SetMaxSizeMethod, { width, height }); + } + + /** + * Sets the minimum size of the window. + * + * @param width - The desired minimum width of the window. + * @param height - The desired minimum height of the window. + */ + SetMinSize(width: number, height: number): Promise { + return this[callerSym](SetMinSizeMethod, { width, height }); + } + + /** + * Sets the relative position of the window to the screen. + * + * @param x - The desired horizontal relative position of the window. + * @param y - The desired vertical relative position of the window. + */ + SetRelativePosition(x: number, y: number): Promise { + return this[callerSym](SetRelativePositionMethod, { x, y }); + } + + /** + * Sets whether the window is resizable. + * + * @param resizable - Whether the window should be resizable. + */ + SetResizable(resizable: boolean): Promise { + return this[callerSym](SetResizableMethod, { resizable }); + } + + /** + * Sets the size of the window. + * + * @param width - The desired width of the window. + * @param height - The desired height of the window. + */ + SetSize(width: number, height: number): Promise { + return this[callerSym](SetSizeMethod, { width, height }); + } + + /** + * Sets the title of the window. + * + * @param title - The desired title of the window. + */ + SetTitle(title: string): Promise { + return this[callerSym](SetTitleMethod, { title }); + } + + /** + * Sets the zoom level of the window. + * + * @param zoom - The desired zoom level. + */ + SetZoom(zoom: number): Promise { + return this[callerSym](SetZoomMethod, { zoom }); + } + + /** + * Shows the window. + */ + Show(): Promise { + return this[callerSym](ShowMethod); + } + + /** + * Returns the size of the window. + * + * @returns The current size of the window. + */ + Size(): Promise { + return this[callerSym](SizeMethod); + } + + /** + * Toggles the window between fullscreen and normal. + */ + ToggleFullscreen(): Promise { + return this[callerSym](ToggleFullscreenMethod); + } + + /** + * Toggles the window between maximised and normal. + */ + ToggleMaximise(): Promise { + return this[callerSym](ToggleMaximiseMethod); + } + + /** + * Toggles the window between frameless and normal. + */ + ToggleFrameless(): Promise { + return this[callerSym](ToggleFramelessMethod); + } + + /** + * Un-fullscreens the window. + */ + UnFullscreen(): Promise { + return this[callerSym](UnFullscreenMethod); + } + + /** + * Un-maximises the window. + */ + UnMaximise(): Promise { + return this[callerSym](UnMaximiseMethod); + } + + /** + * Un-minimises the window. + */ + UnMinimise(): Promise { + return this[callerSym](UnMinimiseMethod); + } + + /** + * Returns the width of the window. + * + * @returns The current width of the window. + */ + Width(): Promise { + return this[callerSym](WidthMethod); + } + + /** + * Zooms the window. + */ + Zoom(): Promise { + return this[callerSym](ZoomMethod); + } + + /** + * Increases the zoom level of the webview content. + */ + ZoomIn(): Promise { + return this[callerSym](ZoomInMethod); + } + + /** + * Decreases the zoom level of the webview content. + */ + ZoomOut(): Promise { + return this[callerSym](ZoomOutMethod); + } + + /** + * Resets the zoom level of the webview content. + */ + ZoomReset(): Promise { + return this[callerSym](ZoomResetMethod); + } + + /** + * Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop). + * Gathers information about the drop target element and sends it back to the Go backend. + * + * @param filenames - An array of file paths (strings) that were dropped. + * @param x - The x-coordinate of the drop event. + * @param y - The y-coordinate of the drop event. + */ + HandlePlatformFileDrop(filenames: string[], x: number, y: number): void { + const element = document.elementFromPoint(x, y); + + // NEW: Check if the drop target is a valid dropzone + const dropzoneTarget = getDropzoneElement(element); + + if (!dropzoneTarget) { + console.log(`Wails Runtime: Drop on element (or no element) at ${x},${y} which is not a designated dropzone. Ignoring. Element:`, element); + // No need to call backend if not a valid dropzone target + return; + } + + console.log(`Wails Runtime: Drop on designated dropzone. Element at (${x}, ${y}):`, element, 'Effective dropzone:', dropzoneTarget); + const elementDetails = { + id: dropzoneTarget.id, + classList: Array.from(dropzoneTarget.classList), + attributes: {} as { [key: string]: string }, + }; + for (let i = 0; i < dropzoneTarget.attributes.length; i++) { + const attr = dropzoneTarget.attributes[i]; + elementDetails.attributes[attr.name] = attr.value; + } + + const payload = { + filenames, + x, + y, + elementDetails, + }; + + this[callerSym](WindowDropZoneDropped, payload); + } + + /* Triggers Windows 11 Snap Assist feature (Windows only). + * This is equivalent to pressing Win+Z and shows snap layout options. + */ + SnapAssist(): Promise { + return this[callerSym](SnapAssistMethod); + } +} + +/** + * The window within which the script is running. + */ +const thisWindow = new Window(''); + +// NEW: Global Drag Event Listeners +function setupGlobalDropzoneListeners() { + const docElement = document.documentElement; + let dragEnterCounter = 0; // To handle dragenter/dragleave on child elements + + docElement.addEventListener('dragenter', (event) => { + event.preventDefault(); + if (event.dataTransfer && event.dataTransfer.types.includes('Files')) { + dragEnterCounter++; + const targetElement = document.elementFromPoint(event.clientX, event.clientY); + const dropzone = getDropzoneElement(targetElement); + + // Clear previous hover regardless, then apply new if valid + if (currentHoveredDropzone && currentHoveredDropzone !== dropzone) { + currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS); + } + + if (dropzone) { + dropzone.classList.add(DROPZONE_HOVER_CLASS); + event.dataTransfer.dropEffect = 'copy'; + currentHoveredDropzone = dropzone; + } else { + event.dataTransfer.dropEffect = 'none'; + currentHoveredDropzone = null; // Ensure it's cleared if no dropzone found + } + } + }, false); + + docElement.addEventListener('dragover', (event) => { + event.preventDefault(); // Necessary to allow drop + if (event.dataTransfer && event.dataTransfer.types.includes('Files')) { + // No need to query elementFromPoint again if already handled by dragenter correctly + // Just ensure dropEffect is continuously set based on currentHoveredDropzone + if (currentHoveredDropzone) { + // Re-apply class just in case it was removed by some other JS + if(!currentHoveredDropzone.classList.contains(DROPZONE_HOVER_CLASS)) { + currentHoveredDropzone.classList.add(DROPZONE_HOVER_CLASS); + } + event.dataTransfer.dropEffect = 'copy'; + } else { + event.dataTransfer.dropEffect = 'none'; + } + } + }, false); + + docElement.addEventListener('dragleave', (event) => { + event.preventDefault(); + if (event.dataTransfer && event.dataTransfer.types.includes('Files')) { + dragEnterCounter--; + // Only remove hover if drag truly left the window or the last dropzone + if (dragEnterCounter === 0 || event.relatedTarget === null || (currentHoveredDropzone && !currentHoveredDropzone.contains(event.relatedTarget as Node))) { + if (currentHoveredDropzone) { + currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS); + currentHoveredDropzone = null; + } + dragEnterCounter = 0; // Reset counter if it went negative or left window + } + } + }, false); + + docElement.addEventListener('drop', (event) => { + event.preventDefault(); // Prevent default browser file handling + dragEnterCounter = 0; // Reset counter + if (currentHoveredDropzone) { + currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS); + currentHoveredDropzone = null; + } + // The actual drop processing is initiated by the native side calling HandlePlatformFileDrop + // HandlePlatformFileDrop will then check if the drop was on a valid zone. + }, false); +} + +// Initialize listeners when the script loads +if (typeof window !== "undefined" && typeof document !== "undefined") { + setupGlobalDropzoneListeners(); +} + +export default thisWindow; diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/wml.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/wml.ts new file mode 100644 index 000000000..bdf7376cf --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/wml.ts @@ -0,0 +1,209 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import { OpenURL } from "./browser.js"; +import { Question } from "./dialogs.js"; +import { Emit } from "./events.js"; +import { canAbortListeners, whenReady } from "./utils.js"; +import Window from "./window.js"; + +/** + * Sends an event with the given name and optional data. + * + * @param eventName - - The name of the event to send. + * @param [data=null] - - Optional data to send along with the event. + */ +function sendEvent(eventName: string, data: any = null): void { + Emit(eventName, data); +} + +/** + * Calls a method on a specified window. + * + * @param windowName - The name of the window to call the method on. + * @param methodName - The name of the method to call. + */ +function callWindowMethod(windowName: string, methodName: string) { + const targetWindow = Window.Get(windowName); + const method = (targetWindow as any)[methodName]; + + if (typeof method !== "function") { + console.error(`Window method '${methodName}' not found`); + return; + } + + try { + method.call(targetWindow); + } catch (e) { + console.error(`Error calling window method '${methodName}': `, e); + } +} + +/** + * Responds to a triggering event by running appropriate WML actions for the current target. + */ +function onWMLTriggered(ev: Event): void { + const element = ev.currentTarget as Element; + + function runEffect(choice = "Yes") { + if (choice !== "Yes") + return; + + const eventType = element.getAttribute('wml-event') || element.getAttribute('data-wml-event'); + const targetWindow = element.getAttribute('wml-target-window') || element.getAttribute('data-wml-target-window') || ""; + const windowMethod = element.getAttribute('wml-window') || element.getAttribute('data-wml-window'); + const url = element.getAttribute('wml-openurl') || element.getAttribute('data-wml-openurl'); + + if (eventType !== null) + sendEvent(eventType); + if (windowMethod !== null) + callWindowMethod(targetWindow, windowMethod); + if (url !== null) + void OpenURL(url); + } + + const confirm = element.getAttribute('wml-confirm') || element.getAttribute('data-wml-confirm'); + + if (confirm) { + Question({ + Title: "Confirm", + Message: confirm, + Detached: false, + Buttons: [ + { Label: "Yes" }, + { Label: "No", IsDefault: true } + ] + }).then(runEffect); + } else { + runEffect(); + } +} + +// Private field names. +const controllerSym = Symbol("controller"); +const triggerMapSym = Symbol("triggerMap"); +const elementCountSym = Symbol("elementCount"); + +/** + * AbortControllerRegistry does not actually remember active event listeners: instead + * it ties them to an AbortSignal and uses an AbortController to remove them all at once. + */ +class AbortControllerRegistry { + // Private fields. + [controllerSym]: AbortController; + + constructor() { + this[controllerSym] = new AbortController(); + } + + /** + * Returns an options object for addEventListener that ties the listener + * to the AbortSignal from the current AbortController. + * + * @param element - An HTML element + * @param triggers - The list of active WML trigger events for the specified elements + */ + set(element: Element, triggers: string[]): AddEventListenerOptions { + return { signal: this[controllerSym].signal }; + } + + /** + * Removes all registered event listeners and resets the registry. + */ + reset(): void { + this[controllerSym].abort(); + this[controllerSym] = new AbortController(); + } +} + +/** + * WeakMapRegistry maps active trigger events to each DOM element through a WeakMap. + * This ensures that the mapping remains private to this module, while still allowing garbage + * collection of the involved elements. + */ +class WeakMapRegistry { + /** Stores the current element-to-trigger mapping. */ + [triggerMapSym]: WeakMap; + /** Counts the number of elements with active WML triggers. */ + [elementCountSym]: number; + + constructor() { + this[triggerMapSym] = new WeakMap(); + this[elementCountSym] = 0; + } + + /** + * Sets active triggers for the specified element. + * + * @param element - An HTML element + * @param triggers - The list of active WML trigger events for the specified element + */ + set(element: Element, triggers: string[]): AddEventListenerOptions { + if (!this[triggerMapSym].has(element)) { this[elementCountSym]++; } + this[triggerMapSym].set(element, triggers); + return {}; + } + + /** + * Removes all registered event listeners. + */ + reset(): void { + if (this[elementCountSym] <= 0) + return; + + for (const element of document.body.querySelectorAll('*')) { + if (this[elementCountSym] <= 0) + break; + + const triggers = this[triggerMapSym].get(element); + if (triggers != null) { this[elementCountSym]--; } + + for (const trigger of triggers || []) + element.removeEventListener(trigger, onWMLTriggered); + } + + this[triggerMapSym] = new WeakMap(); + this[elementCountSym] = 0; + } +} + +const triggerRegistry = canAbortListeners() ? new AbortControllerRegistry() : new WeakMapRegistry(); + +/** + * Adds event listeners to the specified element. + */ +function addWMLListeners(element: Element): void { + const triggerRegExp = /\S+/g; + const triggerAttr = (element.getAttribute('wml-trigger') || element.getAttribute('data-wml-trigger') || "click"); + const triggers: string[] = []; + + let match; + while ((match = triggerRegExp.exec(triggerAttr)) !== null) + triggers.push(match[0]); + + const options = triggerRegistry.set(element, triggers); + for (const trigger of triggers) + element.addEventListener(trigger, onWMLTriggered, options); +} + +/** + * Schedules an automatic reload of WML to be performed as soon as the document is fully loaded. + */ +export function Enable(): void { + whenReady(Reload); +} + +/** + * Reloads the WML page by adding necessary event listeners and browser listeners. + */ +export function Reload(): void { + triggerRegistry.reset(); + document.body.querySelectorAll('[wml-event], [wml-window], [wml-openurl], [data-wml-event], [data-wml-window], [data-wml-openurl]').forEach(addWMLListeners); +} diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/tsconfig.json b/v3/internal/runtime/desktop/@wailsio/runtime/tsconfig.json new file mode 100644 index 000000000..1175a4af4 --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/tsconfig.json @@ -0,0 +1,36 @@ +{ + "include": ["./src/**/*"], + "exclude": ["./src/**/*.test.*"], + "compilerOptions": { + "composite": true, + + "allowJs": false, + + "noEmitOnError": true, + "declaration": true, + "declarationMap": false, + "declarationDir": "types", + "outDir": "dist", + "rootDir": "src", + + "target": "ES2017", + "module": "ES2015", + "moduleResolution": "bundler", + "isolatedModules": true, + "verbatimModuleSyntax": true, + "stripInternal": true, + + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + } +} \ No newline at end of file diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/vitest.config.ts b/v3/internal/runtime/desktop/@wailsio/runtime/vitest.config.ts new file mode 100644 index 000000000..efb60170a --- /dev/null +++ b/v3/internal/runtime/desktop/@wailsio/runtime/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: 'happy-dom', + testTimeout: 200 + }, +}); diff --git a/v3/internal/runtime/desktop/README.md b/v3/internal/runtime/desktop/README.md new file mode 100644 index 000000000..0ca8ed53e --- /dev/null +++ b/v3/internal/runtime/desktop/README.md @@ -0,0 +1,10 @@ +# README + +The `index.js` file in the `compiled` directory is the entrypoint for the `runtime.js` file that may be +loaded at runtime. This will add `window.wails` and `window._wails` to the global scope. + +NOTE: It is preferable to use the `@wailsio/runtime` package to use the runtime. + +⚠️ Do not rebuild the runtime manually after updating TS code: +the CI pipeline will take care of this. +PRs that touch build artifacts will be blocked from merging. \ No newline at end of file diff --git a/v3/internal/runtime/desktop/compiled/main.js b/v3/internal/runtime/desktop/compiled/main.js new file mode 100644 index 000000000..d2b21dca1 --- /dev/null +++ b/v3/internal/runtime/desktop/compiled/main.js @@ -0,0 +1,22 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +import * as Runtime from "../@wailsio/runtime/src"; + +// NOTE: the following methods MUST be imported explicitly because of how esbuild injection works +import { Enable as EnableWML } from "../@wailsio/runtime/src/wml"; +import { debugLog } from "../@wailsio/runtime/src/utils"; + +window.wails = Runtime; +EnableWML(); + +if (DEBUG) { + debugLog("Wails Runtime Loaded") +} diff --git a/v3/internal/runtime/runtime.go b/v3/internal/runtime/runtime.go new file mode 100644 index 000000000..eee7f230c --- /dev/null +++ b/v3/internal/runtime/runtime.go @@ -0,0 +1,7 @@ +package runtime + +var runtimeInit = `window._wails=window._wails||{};window.wails=window.wails||{};` + +func Core() string { + return runtimeInit + flags + invoke + environment +} diff --git a/v3/internal/runtime/runtime_darwin.go b/v3/internal/runtime/runtime_darwin.go new file mode 100644 index 000000000..f40c93d3e --- /dev/null +++ b/v3/internal/runtime/runtime_darwin.go @@ -0,0 +1,6 @@ +//go:build darwin + +package runtime + +var invoke = "window._wails.invoke=function(msg){window.webkit.messageHandlers.external.postMessage(msg);};" +var flags = "" diff --git a/v3/internal/runtime/runtime_dev.go b/v3/internal/runtime/runtime_dev.go new file mode 100644 index 000000000..bb52628cf --- /dev/null +++ b/v3/internal/runtime/runtime_dev.go @@ -0,0 +1,10 @@ +//go:build !production + +package runtime + +import ( + "fmt" + "runtime" +) + +var environment = fmt.Sprintf(`window._wails.environment={"OS":"%s","Arch":"%s","Debug":true};`, runtime.GOOS, runtime.GOARCH) diff --git a/v3/internal/runtime/runtime_linux.go b/v3/internal/runtime/runtime_linux.go new file mode 100644 index 000000000..7d9f32569 --- /dev/null +++ b/v3/internal/runtime/runtime_linux.go @@ -0,0 +1,6 @@ +//go:build linux + +package runtime + +var invoke = "window._wails.invoke=window.webkit.messageHandlers.external.postMessage;" +var flags = "" diff --git a/v3/internal/runtime/runtime_prod.go b/v3/internal/runtime/runtime_prod.go new file mode 100644 index 000000000..ec3613551 --- /dev/null +++ b/v3/internal/runtime/runtime_prod.go @@ -0,0 +1,8 @@ +//go:build production + +package runtime + +import "fmt" +import goruntime "runtime" + +var environment = fmt.Sprintf(`window._wails.environment={"OS":"%s","Arch":"%s","Debug":false};`, goruntime.GOOS, goruntime.GOARCH) diff --git a/v3/internal/runtime/runtime_windows.go b/v3/internal/runtime/runtime_windows.go new file mode 100644 index 000000000..f706aa3b1 --- /dev/null +++ b/v3/internal/runtime/runtime_windows.go @@ -0,0 +1,14 @@ +//go:build windows + +package runtime + +import ( + "fmt" + "github.com/wailsapp/wails/v3/pkg/w32" +) + +var invoke = `window._wails.invoke=window.chrome.webview.postMessage;` +var flags = fmt.Sprintf( + `window._wails.flags={"system":{"resizeHandleWidth":%d,"resizeHandleHeight":%d}};`, + w32.GetSystemMetrics(w32.SM_CXSIZEFRAME), + w32.GetSystemMetrics(w32.SM_CYSIZEFRAME)) diff --git a/v3/internal/s/s.go b/v3/internal/s/s.go new file mode 100644 index 000000000..1ce187e07 --- /dev/null +++ b/v3/internal/s/s.go @@ -0,0 +1,456 @@ +package s + +import ( + "crypto/md5" + "fmt" + "github.com/google/shlex" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" +) + +var ( + Output io.Writer = io.Discard + IndentSize int + originalOutput io.Writer + currentIndent int + dryRun bool + deferred []func() +) + +func checkError(err error) { + if err != nil { + println("\nERROR:", err.Error()) + os.Exit(1) + } +} + +func mute() { + originalOutput = Output + Output = io.Discard +} + +func unmute() { + Output = originalOutput +} + +func indent() { + currentIndent += IndentSize +} + +func unindent() { + currentIndent -= IndentSize +} + +func log(message string, args ...interface{}) { + indent := strings.Repeat(" ", currentIndent) + _, err := fmt.Fprintf(Output, indent+message+"\n", args...) + checkError(err) +} + +// RENAME a file or directory +func RENAME(source string, target string) { + log("RENAME %s -> %s", source, target) + err := os.Rename(source, target) + checkError(err) +} + +// MUSTDELETE a file. +func MUSTDELETE(filename string) { + log("DELETE %s", filename) + err := os.Remove(filepath.Join(CWD(), filename)) + checkError(err) +} + +// DELETE a file. +func DELETE(filename string) { + log("DELETE %s", filename) + _ = os.Remove(filepath.Join(CWD(), filename)) +} + +func CONTAINS(list string, item string) bool { + result := strings.Contains(list, item) + listTrimmed := list + if len(listTrimmed) > 30 { + listTrimmed = listTrimmed[:30] + "..." + } + log("CONTAINS %s in %s: %t", item, listTrimmed, result) + return result +} + +func SETENV(key string, value string) { + log("SETENV %s=%s", key, value) + err := os.Setenv(key, value) + checkError(err) +} + +func CD(dir string) { + err := os.Chdir(dir) + checkError(err) + log("CD %s", dir) +} +func MKDIR(path string, mode ...os.FileMode) { + var perms os.FileMode + perms = 0755 + if len(mode) == 1 { + perms = mode[0] + } + log("MKDIR %s (perms: %v)", path, perms) + err := os.MkdirAll(path, perms) + checkError(err) +} + +// ENDIR ensures that the path gets created if it doesn't exist +func ENDIR(path string, mode ...os.FileMode) { + var perms os.FileMode + perms = 0755 + if len(mode) == 1 { + perms = mode[0] + } + _ = os.MkdirAll(path, perms) +} + +// COPYDIR recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. +// Symlinks are ignored and skipped. +// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 +func COPYDIR(src string, dst string) { + log("COPYDIR %s -> %s", src, dst) + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + checkError(err) + if !si.IsDir() { + checkError(fmt.Errorf("source is not a directory")) + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + checkError(err) + } + if err == nil { + checkError(fmt.Errorf("destination already exists")) + } + + indent() + MKDIR(dst) + + entries, err := os.ReadDir(src) + checkError(err) + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + COPYDIR(srcPath, dstPath) + } else { + // Skip symlinks. + if entry.Type()&os.ModeSymlink != 0 { + continue + } + + COPY(srcPath, dstPath) + } + } + unindent() +} + +// COPYDIR2 recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory can exist. +// Symlinks are ignored and skipped. +// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 +func COPYDIR2(src string, dst string) { + log("COPYDIR %s -> %s", src, dst) + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + checkError(err) + if !si.IsDir() { + checkError(fmt.Errorf("source is not a directory")) + } + + indent() + MKDIR(dst) + + entries, err := os.ReadDir(src) + checkError(err) + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + COPYDIR(srcPath, dstPath) + } else { + // Skip symlinks. + if entry.Type()&os.ModeSymlink != 0 { + continue + } + + COPY(srcPath, dstPath) + } + } + unindent() +} + +func SYMLINK(source string, target string) { + // trim string to first 30 chars + var trimTarget = target + if len(trimTarget) > 30 { + trimTarget = trimTarget[:30] + "..." + } + log("SYMLINK %s -> %s", source, trimTarget) + err := os.Symlink(source, target) + checkError(err) +} + +// COPY file from source to target +func COPY(source string, target string) { + log("COPY %s -> %s", source, target) + src, err := os.Open(source) + checkError(err) + defer closefile(src) + if ISDIR(target) { + target = filepath.Join(target, filepath.Base(source)) + } + d, err := os.Create(target) + checkError(err) + _, err = io.Copy(d, src) + checkError(err) +} + +// Move file from source to target +func MOVE(source string, target string) { + // If target is a directory, append the source filename + if ISDIR(target) { + target = filepath.Join(target, filepath.Base(source)) + } + log("MOVE %s -> %s", source, target) + err := os.Rename(source, target) + checkError(err) +} + +func CWD() string { + result, err := os.Getwd() + checkError(err) + log("CWD %s", result) + return result +} + +func RMDIR(target string) { + log("RMDIR %s", target) + err := os.RemoveAll(target) + checkError(err) +} + +func RM(target string) { + log("RM %s", target) + err := os.Remove(target) + checkError(err) +} + +func ECHO(message string) { + println(message) +} + +func TOUCH(filepath string) { + log("TOUCH %s", filepath) + f, err := os.Create(filepath) + checkError(err) + closefile(f) +} + +func EXEC(command string) ([]byte, error) { + log("EXEC %s", command) + + // Split input using shlex + args, err := shlex.Split(command) + checkError(err) + // Execute command + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = CWD() + cmd.Env = os.Environ() + return cmd.CombinedOutput() +} + +func CHMOD(path string, mode os.FileMode) { + log("CHMOD %s %v", path, mode) + err := os.Chmod(path, mode) + checkError(err) +} + +// EXISTS - Returns true if the given path exists +func EXISTS(path string) bool { + _, err := os.Lstat(path) + log("EXISTS %s -> %t", path, err == nil) + return err == nil +} + +// ISDIR returns true if the given directory exists +func ISDIR(path string) bool { + fi, err := os.Lstat(path) + if err != nil { + return false + } + + return fi.Mode().IsDir() +} + +// ISDIREMPTY returns true if the given directory is empty +func ISDIREMPTY(dir string) bool { + + // CREDIT: https://stackoverflow.com/a/30708914/8325411 + f, err := os.Open(dir) + checkError(err) + defer closefile(f) + + _, err = f.Readdirnames(1) // Or f.Readdir(1) + if err == io.EOF { + return true + } + return false +} + +// ISFILE returns true if the given file exists +func ISFILE(path string) bool { + fi, err := os.Lstat(path) + if err != nil { + return false + } + + return fi.Mode().IsRegular() +} + +// SUBDIRS returns a list of subdirectories for the given directory +func SUBDIRS(rootDir string) []string { + var result []string + + // Iterate root dir + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + checkError(err) + // If we have a directory, save it + if info.IsDir() { + result = append(result, path) + } + return nil + }) + checkError(err) + return result +} + +// SAVESTRING will create a file with the given string +func SAVESTRING(filename string, data string) { + log("SAVESTRING %s", filename) + mute() + SAVEBYTES(filename, []byte(data)) + unmute() +} + +// LOADSTRING returns the contents of the given filename as a string +func LOADSTRING(filename string) string { + log("LOADSTRING %s", filename) + mute() + data := LOADBYTES(filename) + unmute() + return string(data) +} + +// SAVEBYTES will create a file with the given string +func SAVEBYTES(filename string, data []byte) { + log("SAVEBYTES %s", filename) + err := os.WriteFile(filename, data, 0755) + checkError(err) +} + +// LOADBYTES returns the contents of the given filename as a string +func LOADBYTES(filename string) []byte { + log("LOADBYTES %s", filename) + data, err := os.ReadFile(filename) + checkError(err) + return data +} + +func closefile(f *os.File) { + err := f.Close() + checkError(err) +} + +// MD5FILE returns the md5sum of the given file +func MD5FILE(filename string) string { + f, err := os.Open(filename) + checkError(err) + defer closefile(f) + + h := md5.New() + _, err = io.Copy(h, f) + checkError(err) + + return fmt.Sprintf("%x", h.Sum(nil)) +} + +// Sub is the substitution type +type Sub map[string]string + +// REPLACEALL replaces all substitution keys with associated values in the given file +func REPLACEALL(filename string, substitutions Sub) { + log("REPLACEALL %s (%v)", filename, substitutions) + data := LOADSTRING(filename) + for old, newText := range substitutions { + data = strings.ReplaceAll(data, old, newText) + } + SAVESTRING(filename, data) +} + +func DOWNLOAD(url string, target string) { + log("DOWNLOAD %s -> %s", url, target) + // create HTTP client + resp, err := http.Get(url) + checkError(err) + defer resp.Body.Close() + + out, err := os.Create(target) + checkError(err) + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + checkError(err) +} + +func FINDFILES(root string, filenames ...string) []string { + var result []string + // Walk the root directory trying to find all the files + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + checkError(err) + // If we have a file, check if it is in the list + if info.Mode().IsRegular() { + for _, filename := range filenames { + if info.Name() == filename { + result = append(result, path) + } + } + } + return nil + }) + checkError(err) + log("FINDFILES in %s -> [%v]", root, strings.Join(result, ", ")) + return result +} + +func DEFER(fn func()) { + log("DEFER") + deferred = append(deferred, fn) +} + +func CALLDEFER() { + log("CALLDEFER") + for _, fn := range deferred { + fn() + } +} diff --git a/v3/internal/service/service.go b/v3/internal/service/service.go new file mode 100644 index 000000000..76c1c2560 --- /dev/null +++ b/v3/internal/service/service.go @@ -0,0 +1,36 @@ +package service + +import ( + "embed" + "fmt" + "io/fs" + "os" + "path/filepath" + + "github.com/wailsapp/wails/v3/internal/flags" + + "github.com/leaanthony/gosod" + + "github.com/samber/lo" +) + +//go:embed template +var serviceTemplate embed.FS + +type TemplateOptions struct { + *flags.ServiceInit +} + +func Install(options *flags.ServiceInit) error { + + if options.OutputDir == "." || options.OutputDir == "" { + options.OutputDir = filepath.Join(lo.Must(os.Getwd()), options.Name) + } + fmt.Printf("Generating service '%s' into '%s'\n", options.Name, options.OutputDir) + tfs, err := fs.Sub(serviceTemplate, "template") + if err != nil { + return err + } + + return gosod.New(tfs).Extract(options.OutputDir, options) +} diff --git a/v3/internal/service/template/README.tmpl.md b/v3/internal/service/template/README.tmpl.md new file mode 100644 index 000000000..065021b6a --- /dev/null +++ b/v3/internal/service/template/README.tmpl.md @@ -0,0 +1,129 @@ +# Wails v3 Service Template + +This README provides an overview of the Wails v3 service template and explains how to adapt it to create your own custom service. + +## Overview + +The service template provides a basic structure for creating a Wails v3 service. A service in Wails v3 is a Go package that can be integrated into your Wails application to provide specific functionality, handle HTTP requests, and interact with the frontend. + +## Template Structure + +The template defines a `MyService` struct and several methods: + +### MyService Struct + +```go +type MyService struct { + ctx context.Context + options application.ServiceOptions +} +``` + +This is the main service struct. You can rename it to better reflect your service's purpose. The struct holds a context and service options, which are set during startup. + +### ServiceName Method + +```go +func (p *MyService) ServiceName() string +``` + +This method returns the name of the service. It's used to identify the service within the Wails application. + +### ServiceStartup Method + +```go +func (p *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error +``` + +This method is called when the app is starting up. Use it to initialize resources, set up connections, or perform any necessary setup tasks. +It receives a context and service options, which are stored in the service struct. + +### ServiceShutdown Method + +```go +func (p *MyService) ServiceShutdown() error +``` + +This method is called when the app is shutting down. Use it to clean up resources, close connections, or perform any necessary cleanup tasks. + +### ServeHTTP Method + +```go +func (p *MyService) ServeHTTP(w http.ResponseWriter, r *http.Request) +``` + +This method handles HTTP requests to the service. It's called when the frontend makes an HTTP request to the backend +at the path specified in the `Route` field of the service options. + +### Service Methods + +```go +func (p *MyService) Greet(name string) string +``` + +This is an example of a service method. You can add as many methods as you need. These methods can be called from the frontend. + +## Adapting the Template + +To create your own service: + +1. Rename the `MyService` struct to reflect your service's purpose (e.g., `DatabaseService`, `AuthService`). +2. Update the `ServiceName` method to return your service's unique identifier. +3. Implement the `ServiceStartup` method to initialize your service. This might include setting up database connections, loading configuration, etc. +4. If needed, implement the `ServiceShutdown` method to properly clean up resources when the application closes. +5. If your service needs to handle HTTP requests, implement the `ServeHTTP` method. Use this to create API endpoints, serve files, or handle any HTTP interactions. +6. Add your own methods to the service. These can include database operations, business logic, or any functionality your service needs to provide. +7. If your service requires configuration, consider adding a `Config` struct and a `New` function to create and configure your service. + +## Example: Database Service + +Here's how you might adapt the template for a database service: + +```go +type DatabaseService struct { + ctx context.Context + options application.ServiceOptions + db *sql.DB +} + +func (s *DatabaseService) Name() string { + return "github.com/myname/DatabaseService" +} + +func (s *DatabaseService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + s.ctx = ctx + s.options = options + // Initialize database connection + var err error + s.db, err = sql.Open("mysql", "user:password@/dbname") + return err +} + +func (s *DatabaseService) ServiceShutdown() error { + return s.db.Close() +} + +func (s *DatabaseService) GetUser(id int) (User, error) { + // Implement database query +} + +// Add more methods as needed +``` + +## Long-running tasks + +If your service needs to perform long-running tasks, consider using goroutines and channels to manage these tasks. +You can use the `context.Context` to listen for when the application shuts down: + +```go +func (s *DatabaseService) longRunningTask() { + for { + select { + case <-s.ctx.Done(): + // Cleanup and exit + return + // Perform long-running task + } + } +} +``` diff --git a/v3/internal/service/template/go.mod.tmpl b/v3/internal/service/template/go.mod.tmpl new file mode 100644 index 000000000..dc8719753 --- /dev/null +++ b/v3/internal/service/template/go.mod.tmpl @@ -0,0 +1,12 @@ +module {{.Name}} + +go 1.23 + +require github.com/wailsapp/wails/v3 v3.0.0-alpha.7 + +require ( + github.com/imdario/mergo v0.3.12 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect +) diff --git a/v3/internal/service/template/go.sum b/v3/internal/service/template/go.sum new file mode 100644 index 000000000..991eadf04 --- /dev/null +++ b/v3/internal/service/template/go.sum @@ -0,0 +1,20 @@ +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +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/v3 v3.0.0-alpha.7 h1:LNX2EnbxTEYJYICJT8UkuzoGVNalRizTNGBY47endmk= +github.com/wailsapp/wails/v3 v3.0.0-alpha.7/go.mod h1:lBz4zedFxreJBoVpMe9u89oo4IE3IlyHJg5rOWnGNR0= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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/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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/v3/internal/service/template/service.go.tmpl b/v3/internal/service/template/service.go.tmpl new file mode 100644 index 000000000..781e9efff --- /dev/null +++ b/v3/internal/service/template/service.go.tmpl @@ -0,0 +1,60 @@ +package {{.Name}} + +import ( + "context" + "github.com/wailsapp/wails/v3/pkg/application" +) + +// ---------------- Service Setup ---------------- +// This is the main service struct. It can be named anything you like. +// Both the ServiceStartup() and ServiceShutdown() methods are called synchronously when the app starts and stops. +// Changing the name of this struct will change the name of the services class in the frontend +// Bound methods will exist inside frontend/bindings/github.com/user/{{.Name}} under the name of the struct +type MyService struct{ + ctx context.Context + options application.ServiceOptions +} + +// ServiceName is the name of the service +func (p *MyService) ServiceName() string { + return "{{.Name}}" +} + +// ServiceStartup is called when the app is starting up. You can use this to +// initialise any resources you need. You can also access the application +// instance via the app property. +// OPTIONAL: This method is optional. +func (p *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + p.ctx = ctx + p.options = options + return nil +} + +// ServiceShutdown is called when the app is shutting down via runtime.Quit() call +// You can use this to clean up any resources you have allocated +// OPTIONAL: This method is optional. +func (p *MyService) ServiceShutdown() error { + return nil +} + +// ServeHTTP is called when the app is running and the frontend makes an HTTP request to the backend at the path +// specified in the `Route` field of the service Options. +// OPTIONAL: This method is optional. +func (p *MyService) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // You can use the request to get the path, query parameters, headers, etc. + // You can also use the response to set the status code, headers, body etc. + // Consult the net/http documentation for more information: https://pkg.go.dev/net/http + + // Log the request to the console + log.Printf("Received request: %s %s", r.Method, r.URL.Path) +} + +// ---------------- Service Methods ---------------- +// Service methods are just normal Go methods. You can add as many as you like. +// The only requirement is that they are exported (start with a capital letter). +// You can also return any type that is JSON serializable. +// See https://golang.org/pkg/encoding/json/#Marshal for more information. + +func (p *MyService) Greet(name string) string { + return "Hello " + name +} diff --git a/v3/internal/service/template/service.tmpl.yml b/v3/internal/service/template/service.tmpl.yml new file mode 100644 index 000000000..bd018461e --- /dev/null +++ b/v3/internal/service/template/service.tmpl.yml @@ -0,0 +1,8 @@ +# This is the plugin definition file for the "{{.Name}}" plugin. +Name: "{{.Name}}" +Description: "{{.Description}}" +Author: "{{.Author}}" +Version: "{{.Version}}" +Website: "{{.Website}}" +Repository: "{{.Repository}}" +License: "{{.License}}" diff --git a/v3/internal/signal/signal.go b/v3/internal/signal/signal.go new file mode 100644 index 000000000..8ac9821d0 --- /dev/null +++ b/v3/internal/signal/signal.go @@ -0,0 +1,50 @@ +package signal + +import ( + "fmt" + "log/slog" + "os" + "os/signal" + "syscall" +) + +type SignalHandler struct { + cleanup func() + ExitMessage func(sig os.Signal) string + MaxSignal int + Logger *slog.Logger + LogLevel slog.Level +} + +func NewSignalHandler(cleanup func()) *SignalHandler { + return &SignalHandler{ + cleanup: cleanup, + ExitMessage: func(sig os.Signal) string { return fmt.Sprintf("Received signal: %v. Quitting...\n", sig) }, + MaxSignal: 3, + Logger: slog.New(slog.NewTextHandler(os.Stderr, nil)), + LogLevel: slog.LevelInfo, + } +} + +func (s *SignalHandler) Start() { + ctrlC := make(chan os.Signal, s.MaxSignal) + signal.Notify(ctrlC, os.Interrupt, syscall.SIGTERM) + + go func() { + for i := 1; i <= s.MaxSignal; i++ { + sig := <-ctrlC + + if i == 1 { + s.Logger.Info(s.ExitMessage(sig)) + s.cleanup() + break + } else if i < s.MaxSignal { + s.Logger.Info(fmt.Sprintf("Received signal: %v. Press CTRL+C %d more times to force quit...\n", sig, s.MaxSignal-i)) + continue + } else { + s.Logger.Info(fmt.Sprintf("Received signal: %v. Force quitting...\n", sig)) + os.Exit(1) + } + } + }() +} diff --git a/v3/internal/templates/_common/README.md b/v3/internal/templates/_common/README.md new file mode 100644 index 000000000..ad12c3f40 --- /dev/null +++ b/v3/internal/templates/_common/README.md @@ -0,0 +1,59 @@ +# Welcome to Your New Wails3 Project! + +Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running. + +## Getting Started + +1. Navigate to your project directory in the terminal. + +2. To run your application in development mode, use the following command: + + ``` + wails3 dev + ``` + + This will start your application and enable hot-reloading for both frontend and backend changes. + +3. To build your application for production, use: + + ``` + wails3 build + ``` + + This will create a production-ready executable in the `build` directory. + +## Exploring Wails3 Features + +Now that you have your project set up, it's time to explore the features that Wails3 offers: + +1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications. + +2. **Run an example**: To run any of the examples, navigate to the example's directory and use: + + ``` + go run . + ``` + + Note: Some examples may be under development during the alpha phase. + +3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3.wails.io/) for in-depth guides and API references. + +4. **Join the community**: Have questions or want to share your progress? Join the [Wails Discord](https://discord.gg/JDdSxwjhGf) or visit the [Wails discussions on GitHub](https://github.com/wailsapp/wails/discussions). + +## Project Structure + +Take a moment to familiarize yourself with your project structure: + +- `frontend/`: Contains your frontend code (HTML, CSS, JavaScript/TypeScript) +- `main.go`: The entry point of your Go backend +- `app.go`: Define your application structure and methods here +- `wails.json`: Configuration file for your Wails project + +## Next Steps + +1. Modify the frontend in the `frontend/` directory to create your desired UI. +2. Add backend functionality in `main.go`. +3. Use `wails3 dev` to see your changes in real-time. +4. When ready, build your application with `wails3 build`. + +Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community. diff --git a/v3/internal/templates/_common/Taskfile.tmpl.yml b/v3/internal/templates/_common/Taskfile.tmpl.yml new file mode 100644 index 000000000..f075aa3ea --- /dev/null +++ b/v3/internal/templates/_common/Taskfile.tmpl.yml @@ -0,0 +1,34 @@ +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "{{.ProjectName}}" + BIN_DIR: "bin" + VITE_PORT: {{ "'{{.WAILS_VITE_PORT | default 9245}}'" }} + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{ "{{OS}}" }}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{ "{{OS}}" }}:package" + + run: + summary: Runs the application + cmds: + - task: "{{ "{{OS}}" }}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{ "{{.VITE_PORT}}" }} + diff --git a/v3/internal/templates/_common/frontend/Inter Font License.txt b/v3/internal/templates/_common/frontend/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/internal/templates/_common/frontend/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/internal/templates/_common/gitignore.tmpl b/v3/internal/templates/_common/gitignore.tmpl new file mode 100644 index 000000000..ba8194ab6 --- /dev/null +++ b/v3/internal/templates/_common/gitignore.tmpl @@ -0,0 +1,6 @@ +.task +bin +frontend/dist +frontend/node_modules +build/linux/appimage/build +build/windows/nsis/MicrosoftEdgeWebview2Setup.exe \ No newline at end of file diff --git a/v3/internal/templates/_common/go.mod.tmpl b/v3/internal/templates/_common/go.mod.tmpl new file mode 100644 index 000000000..bc6a656d1 --- /dev/null +++ b/v3/internal/templates/_common/go.mod.tmpl @@ -0,0 +1,51 @@ +module changeme + +go 1.24 + +require github.com/wailsapp/wails/v3 {{.WailsVersion}} + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.5 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/wailsapp/go-webview2 v1.0.19 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // 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 + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) +{{if .LocalModulePath}} +replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3 +{{end}} diff --git a/v3/internal/templates/_common/go.sum.tmpl b/v3/internal/templates/_common/go.sum.tmpl new file mode 100644 index 000000000..977b11504 --- /dev/null +++ b/v3/internal/templates/_common/go.sum.tmpl @@ -0,0 +1,146 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= +github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU= +github.com/wailsapp/go-webview2 v1.0.19/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +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/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +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= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/v3/internal/templates/_common/greetservice.go b/v3/internal/templates/_common/greetservice.go new file mode 100644 index 000000000..8972c39cd --- /dev/null +++ b/v3/internal/templates/_common/greetservice.go @@ -0,0 +1,7 @@ +package main + +type GreetService struct{} + +func (g *GreetService) Greet(name string) string { + return "Hello " + name + "!" +} diff --git a/v3/internal/templates/_common/main.go.tmpl b/v3/internal/templates/_common/main.go.tmpl new file mode 100644 index 000000000..be9310930 --- /dev/null +++ b/v3/internal/templates/_common/main.go.tmpl @@ -0,0 +1,77 @@ +package main + +import ( + "embed" + _ "embed" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed all:frontend/dist +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + app := application.New(application.Options{ + Name: "{{.ProjectName}}", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(&GreetService{}), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + // Create a goroutine that emits an event containing the current time every second. + // The frontend can listen to this event and update the UI accordingly. + go func() { + for { + now := time.Now().Format(time.RFC1123) + app.Event.Emit("time", now) + time.Sleep(time.Second) + } + }() + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/internal/templates/base/NEXTSTEPS.md b/v3/internal/templates/base/NEXTSTEPS.md new file mode 100644 index 000000000..6b2e29a08 --- /dev/null +++ b/v3/internal/templates/base/NEXTSTEPS.md @@ -0,0 +1,3 @@ +# Next Steps + +For a full guide on how to create templates, see [Creating Custom Templates](https://v3.wails.io/guides/custom-templates). \ No newline at end of file diff --git a/v3/internal/templates/base/frontend/.gitignore b/v3/internal/templates/base/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/base/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/base/frontend/index.html b/v3/internal/templates/base/frontend/index.html new file mode 100644 index 000000000..b81d9729f --- /dev/null +++ b/v3/internal/templates/base/frontend/index.html @@ -0,0 +1,35 @@ + + + + + + + + Wails App + + +
+ +

Wails + Javascript

+
+
Please enter your name below 👇
+
+ + +
+
+ +
+ + + diff --git a/v3/internal/templates/base/frontend/main.js b/v3/internal/templates/base/frontend/main.js new file mode 100644 index 000000000..c24b3b1ef --- /dev/null +++ b/v3/internal/templates/base/frontend/main.js @@ -0,0 +1,21 @@ +import {GreetService} from "./bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +const resultElement = document.getElementById('result'); +const timeElement = document.getElementById('time'); + +window.doGreet = () => { + let name = document.getElementById('name').value; + if (!name) { + name = 'anonymous'; + } + GreetService.Greet(name).then((result) => { + resultElement.innerText = result; + }).catch((err) => { + console.log(err); + }); +} + +Events.On('time', (time) => { + timeElement.innerText = time.data; +}); diff --git a/v3/internal/templates/base/frontend/package.json b/v3/internal/templates/base/frontend/package.json new file mode 100644 index 000000000..9ae87549e --- /dev/null +++ b/v3/internal/templates/base/frontend/package.json @@ -0,0 +1,16 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^5.0.0", + "@wailsio/runtime": "latest" + } +} \ No newline at end of file diff --git a/v3/internal/templates/lit-ts/frontend/.gitignore b/v3/internal/templates/lit-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/lit-ts/frontend/index.html b/v3/internal/templates/lit-ts/frontend/index.html new file mode 100644 index 000000000..d01a04294 --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/index.html @@ -0,0 +1,16 @@ + + + + + + + Wails + Lit + TS + + + + + +

Wails + Lit

+
+ + diff --git a/v3/internal/templates/lit-ts/frontend/package.json b/v3/internal/templates/lit-ts/frontend/package.json new file mode 100644 index 000000000..d208c3fbd --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "lit": "^3.1.0" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^5.0.0" + } +} diff --git a/v3/internal/templates/lit-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/lit-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/lit-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/lit-ts/frontend/public/lit.svg b/v3/internal/templates/lit-ts/frontend/public/lit.svg new file mode 100644 index 000000000..4a9c1fe66 --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/public/lit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/lit-ts/frontend/public/style.css b/v3/internal/templates/lit-ts/frontend/public/style.css new file mode 100644 index 000000000..10550a378 --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/public/style.css @@ -0,0 +1,58 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} \ No newline at end of file diff --git a/v3/internal/templates/lit-ts/frontend/public/wails.png b/v3/internal/templates/lit-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/lit-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/lit-ts/frontend/src/my-element.ts b/v3/internal/templates/lit-ts/frontend/src/my-element.ts new file mode 100644 index 000000000..437b74cb8 --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/src/my-element.ts @@ -0,0 +1,174 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {GreetService} from '../bindings/changeme'; +import {Events} from "@wailsio/runtime"; + +/** + * An example element. + * + * @slot - This element has a slot + * @csspart button - The button + */ +@customElement('my-element') +export class MyElement extends LitElement { + + @property() + result: string = 'Please enter your name below 👇' + + @property() + time: string = 'Listening for Time event...' + + @property() + name: string = ''; + + constructor() { + super(); + Events.On('time', (timeValue: { data: string }) => { + this.time = timeValue.data; + }); + } + + + doGreet() { + let name = this.name; + if (!name) { + name = 'anonymous'; + } + GreetService.Greet(name).then((resultValue: string) => { + this.result = resultValue; + }).catch((err: Error) => { + console.log(err); + }); + } + + render() { + return html` +
+ + +
${this.result}
+
+
+ this.name = (e.target as HTMLInputElement).value} type="text" + autocomplete="off"/> + +
+
+ +
+ ` + } + + + static styles = css` + :host { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; + } + + h3 { + font-size: 3em; + line-height: 1.1; + } + + a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; + } + + a:hover { + color: #535bf2; + } + + button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; + } + + .container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .logo { + height: 6em; + padding: 1.5em; + will-change: filter; + } + + .logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); + } + + .logo.lit:hover { + filter: drop-shadow(0 0 2em #325cffaa); + } + + .result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; + } + + .footer { + margin-top: 1rem; + align-content: center; + text-align: center; + } + + .input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; + } + + .input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; + } + + .input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); + } + + .input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + 'my-element': MyElement + } +} diff --git a/v3/internal/templates/lit-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/lit-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/internal/templates/lit-ts/frontend/tsconfig.json b/v3/internal/templates/lit-ts/frontend/tsconfig.json new file mode 100644 index 000000000..12a8b4eb6 --- /dev/null +++ b/v3/internal/templates/lit-ts/frontend/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/v3/internal/templates/lit-ts/template.json b/v3/internal/templates/lit-ts/template.json new file mode 100644 index 000000000..ea0bf57f9 --- /dev/null +++ b/v3/internal/templates/lit-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Lit + Vite (Typescript)", + "shortname": "lit-ts", + "author": "Lea Anthony", + "description": "Lit + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/lit/frontend/.gitignore b/v3/internal/templates/lit/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/lit/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/lit/frontend/index.html b/v3/internal/templates/lit/frontend/index.html new file mode 100644 index 000000000..7993cbcef --- /dev/null +++ b/v3/internal/templates/lit/frontend/index.html @@ -0,0 +1,16 @@ + + + + + + + Wails + Lit + + + + + +

Wails + Lit

+
+ + diff --git a/v3/internal/templates/lit/frontend/package.json b/v3/internal/templates/lit/frontend/package.json new file mode 100644 index 000000000..ec30e751a --- /dev/null +++ b/v3/internal/templates/lit/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "lit": "^3.1.0" + }, + "devDependencies": { + "vite": "^5.0.0" + } +} diff --git a/v3/internal/templates/lit/frontend/public/Inter-Medium.ttf b/v3/internal/templates/lit/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/lit/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/lit/frontend/public/lit.svg b/v3/internal/templates/lit/frontend/public/lit.svg new file mode 100644 index 000000000..4a9c1fe66 --- /dev/null +++ b/v3/internal/templates/lit/frontend/public/lit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/lit/frontend/public/style.css b/v3/internal/templates/lit/frontend/public/style.css new file mode 100644 index 000000000..10550a378 --- /dev/null +++ b/v3/internal/templates/lit/frontend/public/style.css @@ -0,0 +1,58 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} \ No newline at end of file diff --git a/v3/internal/templates/lit/frontend/public/wails.png b/v3/internal/templates/lit/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/lit/frontend/public/wails.png differ diff --git a/v3/internal/templates/lit/frontend/src/my-element.js b/v3/internal/templates/lit/frontend/src/my-element.js new file mode 100644 index 000000000..5c617da3b --- /dev/null +++ b/v3/internal/templates/lit/frontend/src/my-element.js @@ -0,0 +1,159 @@ +import {css, html, LitElement} from 'lit' +import {GreetService} from "../bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +export class MyElement extends LitElement { + static properties = { + name: {type: String}, + result: {type: String}, + time: {type: String}, + }; + + constructor() { + super(); + this.name = ''; + this.result = 'Please enter your name below 👇'; + this.time = 'Listening for Time event...'; + Events.On('time', (timeValue) => { + this.time = timeValue.data; + }); + } + + doGreet() { + let name = this.name; + if (!name) { + name = 'anonymous'; + } + GreetService.Greet(name).then((resultValue) => { + this.result = resultValue; + }).catch((err) => { + console.log(err); + }); + } + + render() { + return html` +
+ + +
${this.result}
+
+
+ this.name = e.target.value} type="text" + autocomplete="off"/> + +
+
+ +
+ `; + } + + static styles = css` + :host { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; + } + + h3 { + font-size: 3em; + line-height: 1.1; + } + + a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; + } + + a:hover { + color: #535bf2; + } + + button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; + } + + .container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .logo { + height: 6em; + padding: 1.5em; + will-change: filter; + } + + .logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); + } + + .logo.lit:hover { + filter: drop-shadow(0 0 2em #325cffaa); + } + + .result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; + } + + .footer { + margin-top: 1rem; + align-content: center; + text-align: center; + } + + .input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; + } + + .input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; + } + + .input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); + } + + .input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); + } + `; +} + +window.customElements.define('my-element', MyElement); diff --git a/v3/internal/templates/lit/template.json b/v3/internal/templates/lit/template.json new file mode 100644 index 000000000..9c3ba3c61 --- /dev/null +++ b/v3/internal/templates/lit/template.json @@ -0,0 +1,9 @@ +{ + "name": "Lit + Vite", + "shortname": "lit", + "author": "Lea Anthony", + "description": "Lit + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/preact-ts/frontend/.gitignore b/v3/internal/templates/preact-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/preact-ts/frontend/index.html b/v3/internal/templates/preact-ts/frontend/index.html new file mode 100644 index 000000000..f4addcc25 --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + Preact + + +
+ + + diff --git a/v3/internal/templates/preact-ts/frontend/package.json b/v3/internal/templates/preact-ts/frontend/package.json new file mode 100644 index 000000000..b5dd75296 --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/package.json @@ -0,0 +1,21 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "preact": "^10.19.3" + }, + "devDependencies": { + "@preact/preset-vite": "^2.7.0", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/preact-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/preact-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/preact-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/preact-ts/frontend/public/preact.svg b/v3/internal/templates/preact-ts/frontend/public/preact.svg new file mode 100644 index 000000000..908f17def --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/public/preact.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/preact-ts/frontend/public/style.css b/v3/internal/templates/preact-ts/frontend/public/style.css new file mode 100644 index 000000000..c4f073382 --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/public/style.css @@ -0,0 +1,158 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.preact:hover { + filter: drop-shadow(0 0 2em #673ab8aa); +} + + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/preact-ts/frontend/public/wails.png b/v3/internal/templates/preact-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/preact-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/preact-ts/frontend/src/app.tsx b/v3/internal/templates/preact-ts/frontend/src/app.tsx new file mode 100644 index 000000000..93fab58d3 --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/src/app.tsx @@ -0,0 +1,55 @@ +import {useEffect, useState} from 'preact/hooks' +import {GreetService} from "../bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +export function App() { + const [name, setName] = useState(''); + const [result, setResult] = useState('Please enter your name below 👇'); + const [time, setTime] = useState('Listening for Time event...'); + + const doGreet = (): void => { + let localName = name; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue: string) => { + setResult(resultValue); + }).catch((err: any) => { + console.log(err); + }); + } + + useEffect(() => { + Events.On('time', (timeValue: any) => { + setTime(timeValue.data); + }); + }, []); + + return ( + <> +
+ +

Wails + Preact

+
{result}
+
+
+ setName(e.currentTarget.value)} + type="text" autocomplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time}

+
+
+ + ) +} diff --git a/v3/internal/templates/preact-ts/frontend/src/main.tsx b/v3/internal/templates/preact-ts/frontend/src/main.tsx new file mode 100644 index 000000000..2af1859fe --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/src/main.tsx @@ -0,0 +1,4 @@ +import { render } from 'preact' +import { App } from './app' + +render(, document.getElementById('app') as HTMLElement) diff --git a/v3/internal/templates/preact-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/preact-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/internal/templates/preact-ts/frontend/tsconfig.json b/v3/internal/templates/preact-ts/frontend/tsconfig.json new file mode 100644 index 000000000..58c0ca7a7 --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "paths": { + "react": ["./node_modules/preact/compat/"], + "react-dom": ["./node_modules/preact/compat/"] + }, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/preact-ts/frontend/tsconfig.node.json b/v3/internal/templates/preact-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/preact-ts/frontend/vite.config.ts b/v3/internal/templates/preact-ts/frontend/vite.config.ts new file mode 100644 index 000000000..29b326faf --- /dev/null +++ b/v3/internal/templates/preact-ts/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [preact()], +}) diff --git a/v3/internal/templates/preact-ts/template.json b/v3/internal/templates/preact-ts/template.json new file mode 100644 index 000000000..e2b867ebc --- /dev/null +++ b/v3/internal/templates/preact-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Preact + Vite (Typescript)", + "shortname": "preact-ts", + "author": "Lea Anthony", + "description": "Preact + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/preact/frontend/.gitignore b/v3/internal/templates/preact/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/preact/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/preact/frontend/index.html b/v3/internal/templates/preact/frontend/index.html new file mode 100644 index 000000000..3657fd5ef --- /dev/null +++ b/v3/internal/templates/preact/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + Preact + + +
+ + + diff --git a/v3/internal/templates/preact/frontend/package.json b/v3/internal/templates/preact/frontend/package.json new file mode 100644 index 000000000..863d1fc23 --- /dev/null +++ b/v3/internal/templates/preact/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "preact": "^10.19.3" + }, + "devDependencies": { + "@preact/preset-vite": "^2.7.0", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/preact/frontend/public/Inter-Medium.ttf b/v3/internal/templates/preact/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/preact/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/preact/frontend/public/preact.svg b/v3/internal/templates/preact/frontend/public/preact.svg new file mode 100644 index 000000000..908f17def --- /dev/null +++ b/v3/internal/templates/preact/frontend/public/preact.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/preact/frontend/public/style.css b/v3/internal/templates/preact/frontend/public/style.css new file mode 100644 index 000000000..c4f073382 --- /dev/null +++ b/v3/internal/templates/preact/frontend/public/style.css @@ -0,0 +1,158 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.preact:hover { + filter: drop-shadow(0 0 2em #673ab8aa); +} + + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/preact/frontend/public/wails.png b/v3/internal/templates/preact/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/preact/frontend/public/wails.png differ diff --git a/v3/internal/templates/preact/frontend/src/app.jsx b/v3/internal/templates/preact/frontend/src/app.jsx new file mode 100644 index 000000000..c8c71f638 --- /dev/null +++ b/v3/internal/templates/preact/frontend/src/app.jsx @@ -0,0 +1,52 @@ +import { useState, useEffect } from 'preact/hooks' +import {GreetService} from "../bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +export function App() { + const [name, setName] = useState(''); + const [result, setResult] = useState('Please enter your name below 👇'); + const [time, setTime] = useState('Listening for Time event...'); + + const doGreet = () => { + let localName = name; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue) => { + setResult(resultValue); + }).catch((err) => { + console.log(err); + }); + } + + useEffect(() => { + Events.On('time', (timeValue) => { + setTime(timeValue.data); + }); + }, []); + + return ( +
+ +

Wails + Preact

+
{result}
+
+
+ setName(e.target.value)} type="text" autoComplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time}

+
+
+ ) +} diff --git a/v3/internal/templates/preact/frontend/src/main.jsx b/v3/internal/templates/preact/frontend/src/main.jsx new file mode 100644 index 000000000..5867d2a14 --- /dev/null +++ b/v3/internal/templates/preact/frontend/src/main.jsx @@ -0,0 +1,4 @@ +import { render } from 'preact' +import { App } from './app' + +render(, document.getElementById('app')) diff --git a/v3/internal/templates/preact/frontend/vite.config.js b/v3/internal/templates/preact/frontend/vite.config.js new file mode 100644 index 000000000..29b326faf --- /dev/null +++ b/v3/internal/templates/preact/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [preact()], +}) diff --git a/v3/internal/templates/preact/template.json b/v3/internal/templates/preact/template.json new file mode 100644 index 000000000..c2e58d779 --- /dev/null +++ b/v3/internal/templates/preact/template.json @@ -0,0 +1,9 @@ +{ + "name": "Preact + Vite", + "shortname": "preact", + "author": "Lea Anthony", + "description": "Preact + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/qwik-ts/frontend/.gitignore b/v3/internal/templates/qwik-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/qwik-ts/frontend/index.html b/v3/internal/templates/qwik-ts/frontend/index.html new file mode 100644 index 000000000..b45ac182d --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Qwik + + +
+ + + diff --git a/v3/internal/templates/qwik-ts/frontend/package.json b/v3/internal/templates/qwik-ts/frontend/package.json new file mode 100644 index 000000000..b3f359a6b --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "quik-ts-latest", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@builder.io/qwik": "^1.3.0", + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/qwik-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/qwik-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/qwik-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/qwik-ts/frontend/public/qwik.svg b/v3/internal/templates/qwik-ts/frontend/public/qwik.svg new file mode 100644 index 000000000..08a46e2da --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/public/qwik.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/v3/internal/templates/qwik-ts/frontend/public/style.css b/v3/internal/templates/qwik-ts/frontend/public/style.css new file mode 100644 index 000000000..c1d8d1a2e --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.qwik:hover { + filter: drop-shadow(0 0 2em #673ab8aa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/qwik-ts/frontend/public/wails.png b/v3/internal/templates/qwik-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/qwik-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/qwik-ts/frontend/src/app.tsx b/v3/internal/templates/qwik-ts/frontend/src/app.tsx new file mode 100644 index 000000000..3315315c2 --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/src/app.tsx @@ -0,0 +1,54 @@ +import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik' +import {GreetService} from "../bindings/changeme"; +import {Events, WML} from "@wailsio/runtime"; + +export const App = component$(() => { + const name = useSignal(''); + const result = useSignal('Please enter your name below 👇'); + const time = useSignal('Listening for Time event...'); + + const doGreet = () => { + let localName = name.value; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue: string) => { + result.value = resultValue; + }).catch((err: any) => { + console.log(err); + }); + } + + useVisibleTask$(() => { + Events.On('time', (timeValue: any) => { + time.value = timeValue.data; + }); + // Reload WML so it picks up the wml tags + WML.Reload(); + }); + + return ( +
+ +

Wails + Qwik

+
{result.value}
+
+
+ name.value = (e.target as HTMLInputElement).value} type="text" autocomplete="off"/> + +
+
+ +
+ ) +}) diff --git a/v3/internal/templates/qwik-ts/frontend/src/main.tsx b/v3/internal/templates/qwik-ts/frontend/src/main.tsx new file mode 100644 index 000000000..2c779e49e --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/src/main.tsx @@ -0,0 +1,6 @@ +import '@builder.io/qwik/qwikloader.js' + +import { render } from '@builder.io/qwik' +import { App } from './app.tsx' + +render(document.getElementById('app') as HTMLElement, ) diff --git a/v3/internal/templates/qwik-ts/frontend/tsconfig.json b/v3/internal/templates/qwik-ts/frontend/tsconfig.json new file mode 100644 index 000000000..3e575f3cc --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "@builder.io/qwik", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/qwik-ts/frontend/tsconfig.node.json b/v3/internal/templates/qwik-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/qwik-ts/frontend/vite.config.js b/v3/internal/templates/qwik-ts/frontend/vite.config.js new file mode 100644 index 000000000..cabd66b01 --- /dev/null +++ b/v3/internal/templates/qwik-ts/frontend/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import { qwikVite } from '@builder.io/qwik/optimizer' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + qwikVite({ + csr: true, + }), + ], +}) diff --git a/v3/internal/templates/qwik-ts/template.json b/v3/internal/templates/qwik-ts/template.json new file mode 100644 index 000000000..fd27f0b0a --- /dev/null +++ b/v3/internal/templates/qwik-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Qwik + TS + Vite", + "shortname": "qwik", + "author": "Lea Anthony", + "description": "Qwik + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/qwik/frontend/.gitignore b/v3/internal/templates/qwik/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/qwik/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/qwik/frontend/index.html b/v3/internal/templates/qwik/frontend/index.html new file mode 100644 index 000000000..7c6c2a226 --- /dev/null +++ b/v3/internal/templates/qwik/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Vite + Qwik + + +
+ + + diff --git a/v3/internal/templates/qwik/frontend/package.json b/v3/internal/templates/qwik/frontend/package.json new file mode 100644 index 000000000..3139e426b --- /dev/null +++ b/v3/internal/templates/qwik/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "qwik-latest", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@builder.io/qwik": "^1.3.0", + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/qwik/frontend/public/Inter-Medium.ttf b/v3/internal/templates/qwik/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/qwik/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/qwik/frontend/public/qwik.svg b/v3/internal/templates/qwik/frontend/public/qwik.svg new file mode 100644 index 000000000..08a46e2da --- /dev/null +++ b/v3/internal/templates/qwik/frontend/public/qwik.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/v3/internal/templates/qwik/frontend/public/style.css b/v3/internal/templates/qwik/frontend/public/style.css new file mode 100644 index 000000000..c1d8d1a2e --- /dev/null +++ b/v3/internal/templates/qwik/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.qwik:hover { + filter: drop-shadow(0 0 2em #673ab8aa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/qwik/frontend/public/wails.png b/v3/internal/templates/qwik/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/qwik/frontend/public/wails.png differ diff --git a/v3/internal/templates/qwik/frontend/src/app.jsx b/v3/internal/templates/qwik/frontend/src/app.jsx new file mode 100644 index 000000000..52dcda7b3 --- /dev/null +++ b/v3/internal/templates/qwik/frontend/src/app.jsx @@ -0,0 +1,54 @@ +import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik' +import {GreetService} from "../bindings/changeme"; +import {Events, WML} from "@wailsio/runtime"; + +export const App = component$(() => { + const name = useSignal(''); + const result = useSignal('Please enter your name below 👇'); + const time = useSignal('Listening for Time event...'); + + const doGreet = () => { + let localName = name.value; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue) => { + result.value = resultValue; + }).catch((err) => { + console.log(err); + }); + } + + useVisibleTask$(() => { + Events.On('time', (timeValue) => { + time.value = timeValue.data; + }); + // Reload WML so it picks up the wml tags + WML.Reload(); + }); + + return ( +
+ +

Wails + Qwik

+
{result.value}
+
+
+ name.value = e.target.value} type="text" autocomplete="off"/> + +
+
+ +
+ ) +}) diff --git a/v3/internal/templates/qwik/frontend/src/main.jsx b/v3/internal/templates/qwik/frontend/src/main.jsx new file mode 100644 index 000000000..779d53be1 --- /dev/null +++ b/v3/internal/templates/qwik/frontend/src/main.jsx @@ -0,0 +1,6 @@ +import '@builder.io/qwik/qwikloader.js' + +import { render } from '@builder.io/qwik' +import { App } from './app.jsx' + +render(document.getElementById('app'), ) diff --git a/v3/internal/templates/qwik/frontend/vite.config.js b/v3/internal/templates/qwik/frontend/vite.config.js new file mode 100644 index 000000000..cabd66b01 --- /dev/null +++ b/v3/internal/templates/qwik/frontend/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import { qwikVite } from '@builder.io/qwik/optimizer' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + qwikVite({ + csr: true, + }), + ], +}) diff --git a/v3/internal/templates/qwik/template.json b/v3/internal/templates/qwik/template.json new file mode 100644 index 000000000..2677702bf --- /dev/null +++ b/v3/internal/templates/qwik/template.json @@ -0,0 +1,9 @@ +{ + "name": "Qwik + Vite", + "shortname": "qwik", + "author": "Lea Anthony", + "description": "Qwik + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/react-swc-ts/frontend/.gitignore b/v3/internal/templates/react-swc-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/react-swc-ts/frontend/index.html b/v3/internal/templates/react-swc-ts/frontend/index.html new file mode 100644 index 000000000..0dba04049 --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + React + TS + + +
+ + + diff --git a/v3/internal/templates/react-swc-ts/frontend/package.json b/v3/internal/templates/react-swc-ts/frontend/package.json new file mode 100644 index 000000000..0dcc8cdcd --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/package.json @@ -0,0 +1,24 @@ +{ + "name": "react-ts-latest", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@vitejs/plugin-react-swc": "^3.5.0", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/react-swc-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/react-swc-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/react-swc-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/react-swc-ts/frontend/public/react.svg b/v3/internal/templates/react-swc-ts/frontend/public/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/public/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/react-swc-ts/frontend/public/style.css b/v3/internal/templates/react-swc-ts/frontend/public/style.css new file mode 100644 index 000000000..0ba9cf5cc --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} diff --git a/v3/internal/templates/react-swc-ts/frontend/public/wails.png b/v3/internal/templates/react-swc-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/react-swc-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/react-swc-ts/frontend/src/App.tsx b/v3/internal/templates/react-swc-ts/frontend/src/App.tsx new file mode 100644 index 000000000..5706ca1c4 --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/src/App.tsx @@ -0,0 +1,56 @@ +import { useState, useEffect } from 'react' +import {GreetService} from "../bindings/changeme"; +import {Events, WML} from "@wailsio/runtime"; + +function App() { + const [name, setName] = useState(''); + const [result, setResult] = useState('Please enter your name below 👇'); + const [time, setTime] = useState('Listening for Time event...'); + + const doGreet = () => { + let localName = name; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue: string) => { + setResult(resultValue); + }).catch((err: any) => { + console.log(err); + }); + } + + useEffect(() => { + Events.On('time', (timeValue: any) => { + setTime(timeValue.data); + }); + // Reload WML so it picks up the wml tags + WML.Reload(); + }, []); + + return ( +
+ +

Wails + React

+
{result}
+
+
+ setName(e.target.value)} type="text" autoComplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time}

+
+
+ ) +} + +export default App diff --git a/v3/internal/templates/react-swc-ts/frontend/src/main.tsx b/v3/internal/templates/react-swc-ts/frontend/src/main.tsx new file mode 100644 index 000000000..3e1823139 --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/src/main.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + , +) diff --git a/v3/internal/templates/react-swc-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/react-swc-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/internal/templates/react-swc-ts/frontend/tsconfig.json b/v3/internal/templates/react-swc-ts/frontend/tsconfig.json new file mode 100644 index 000000000..7dbf437aa --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/react-swc-ts/frontend/tsconfig.node.json b/v3/internal/templates/react-swc-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/react-swc-ts/frontend/vite.config.ts b/v3/internal/templates/react-swc-ts/frontend/vite.config.ts new file mode 100644 index 000000000..861b04b35 --- /dev/null +++ b/v3/internal/templates/react-swc-ts/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react-swc' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/v3/internal/templates/react-swc-ts/template.json b/v3/internal/templates/react-swc-ts/template.json new file mode 100644 index 000000000..c40ff0772 --- /dev/null +++ b/v3/internal/templates/react-swc-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "React + SWC + Vite (Typescript)", + "shortname": "react-swc-ts", + "author": "Lea Anthony", + "description": "React + TS + SWC + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/react-swc/frontend/.gitignore b/v3/internal/templates/react-swc/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/react-swc/frontend/index.html b/v3/internal/templates/react-swc/frontend/index.html new file mode 100644 index 000000000..468143c3d --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + React + + +
+ + + diff --git a/v3/internal/templates/react-swc/frontend/package.json b/v3/internal/templates/react-swc/frontend/package.json new file mode 100644 index 000000000..158ba6880 --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/package.json @@ -0,0 +1,23 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@vitejs/plugin-react-swc": "^3.5.0", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/react-swc/frontend/public/Inter-Medium.ttf b/v3/internal/templates/react-swc/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/react-swc/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/react-swc/frontend/public/react.svg b/v3/internal/templates/react-swc/frontend/public/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/public/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/react-swc/frontend/public/style.css b/v3/internal/templates/react-swc/frontend/public/style.css new file mode 100644 index 000000000..0ba9cf5cc --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} diff --git a/v3/internal/templates/react-swc/frontend/public/wails.png b/v3/internal/templates/react-swc/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/react-swc/frontend/public/wails.png differ diff --git a/v3/internal/templates/react-swc/frontend/src/App.jsx b/v3/internal/templates/react-swc/frontend/src/App.jsx new file mode 100644 index 000000000..52db24562 --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/src/App.jsx @@ -0,0 +1,56 @@ +import { useState, useEffect } from 'react' +import {GreetService} from "../bindings/changeme"; +import {Events, WML} from "@wailsio/runtime"; + +function App() { + const [name, setName] = useState(''); + const [result, setResult] = useState('Please enter your name below 👇'); + const [time, setTime] = useState('Listening for Time event...'); + + const doGreet = () => { + let localName = name; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue) => { + setResult(resultValue); + }).catch((err) => { + console.log(err); + }); + } + + useEffect(() => { + Events.On('time', (timeValue) => { + setTime(timeValue.data); + }); + // Reload WML so it picks up the wml tags + WML.Reload(); + }, []); + + return ( +
+ +

Wails + React

+
{result}
+
+
+ setName(e.target.value)} type="text" autoComplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time}

+
+
+ ) +} + +export default App diff --git a/v3/internal/templates/react-swc/frontend/src/main.jsx b/v3/internal/templates/react-swc/frontend/src/main.jsx new file mode 100644 index 000000000..1943cc824 --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/src/main.jsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + , +) diff --git a/v3/internal/templates/react-swc/frontend/vite.config.js b/v3/internal/templates/react-swc/frontend/vite.config.js new file mode 100644 index 000000000..861b04b35 --- /dev/null +++ b/v3/internal/templates/react-swc/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react-swc' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/v3/internal/templates/react-swc/template.json b/v3/internal/templates/react-swc/template.json new file mode 100644 index 000000000..de25db768 --- /dev/null +++ b/v3/internal/templates/react-swc/template.json @@ -0,0 +1,9 @@ +{ + "name": "React + SWC + Vite", + "shortname": "react-swc", + "author": "Lea Anthony", + "description": "React + SWC + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/react-ts/frontend/.gitignore b/v3/internal/templates/react-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/react-ts/frontend/index.html b/v3/internal/templates/react-ts/frontend/index.html new file mode 100644 index 000000000..0dba04049 --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + React + TS + + +
+ + + diff --git a/v3/internal/templates/react-ts/frontend/package.json b/v3/internal/templates/react-ts/frontend/package.json new file mode 100644 index 000000000..f718c0073 --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/package.json @@ -0,0 +1,24 @@ +{ + "name": "react-ts-latest", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@vitejs/plugin-react": "^4.2.1", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/react-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/react-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/react-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/react-ts/frontend/public/react.svg b/v3/internal/templates/react-ts/frontend/public/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/public/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/react-ts/frontend/public/style.css b/v3/internal/templates/react-ts/frontend/public/style.css new file mode 100644 index 000000000..0ba9cf5cc --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} diff --git a/v3/internal/templates/react-ts/frontend/public/wails.png b/v3/internal/templates/react-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/react-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/react-ts/frontend/src/App.tsx b/v3/internal/templates/react-ts/frontend/src/App.tsx new file mode 100644 index 000000000..edc81f820 --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/src/App.tsx @@ -0,0 +1,56 @@ +import { useState, useEffect } from 'react' +import {GreetService} from "../bindings/changeme"; +import {Events, WML} from "@wailsio/runtime"; + +function App() { + const [name, setName] = useState(''); + const [result, setResult] = useState('Please enter your name below 👇'); + const [time, setTime] = useState('Listening for Time event...'); + + const doGreet = () => { + let localName = name; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue: string) => { + setResult(resultValue); + }).catch((err: any) => { + console.log(err); + }); + } + + useEffect(() => { + Events.On('time', (timeValue: any) => { + setTime(timeValue.data); + }); + // Reload WML so it picks up the wml tags + WML.Reload(); + }, []); + + return ( +
+ +

Wails + React

+
{result}
+
+
+ setName(e.target.value)} type="text" autoComplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time}

+
+
+ ) +} + +export default App diff --git a/v3/internal/templates/react-ts/frontend/src/main.tsx b/v3/internal/templates/react-ts/frontend/src/main.tsx new file mode 100644 index 000000000..3e1823139 --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/src/main.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + , +) diff --git a/v3/internal/templates/react-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/react-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/internal/templates/react-ts/frontend/tsconfig.json b/v3/internal/templates/react-ts/frontend/tsconfig.json new file mode 100644 index 000000000..7dbf437aa --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/react-ts/frontend/tsconfig.node.json b/v3/internal/templates/react-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/react-ts/frontend/vite.config.ts b/v3/internal/templates/react-ts/frontend/vite.config.ts new file mode 100644 index 000000000..5a33944a9 --- /dev/null +++ b/v3/internal/templates/react-ts/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/v3/internal/templates/react-ts/template.json b/v3/internal/templates/react-ts/template.json new file mode 100644 index 000000000..33dd85583 --- /dev/null +++ b/v3/internal/templates/react-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "React + Vite (Typescript)", + "shortname": "react-ts", + "author": "Lea Anthony", + "description": "React + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/react/frontend/.gitignore b/v3/internal/templates/react/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/react/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/react/frontend/index.html b/v3/internal/templates/react/frontend/index.html new file mode 100644 index 000000000..468143c3d --- /dev/null +++ b/v3/internal/templates/react/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + React + + +
+ + + diff --git a/v3/internal/templates/react/frontend/package.json b/v3/internal/templates/react/frontend/package.json new file mode 100644 index 000000000..59d4d62b3 --- /dev/null +++ b/v3/internal/templates/react/frontend/package.json @@ -0,0 +1,23 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@vitejs/plugin-react": "^4.2.1", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/react/frontend/public/Inter-Medium.ttf b/v3/internal/templates/react/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/react/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/react/frontend/public/react.svg b/v3/internal/templates/react/frontend/public/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/v3/internal/templates/react/frontend/public/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/react/frontend/public/style.css b/v3/internal/templates/react/frontend/public/style.css new file mode 100644 index 000000000..0ba9cf5cc --- /dev/null +++ b/v3/internal/templates/react/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} diff --git a/v3/internal/templates/react/frontend/public/wails.png b/v3/internal/templates/react/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/react/frontend/public/wails.png differ diff --git a/v3/internal/templates/react/frontend/src/App.jsx b/v3/internal/templates/react/frontend/src/App.jsx new file mode 100644 index 000000000..52db24562 --- /dev/null +++ b/v3/internal/templates/react/frontend/src/App.jsx @@ -0,0 +1,56 @@ +import { useState, useEffect } from 'react' +import {GreetService} from "../bindings/changeme"; +import {Events, WML} from "@wailsio/runtime"; + +function App() { + const [name, setName] = useState(''); + const [result, setResult] = useState('Please enter your name below 👇'); + const [time, setTime] = useState('Listening for Time event...'); + + const doGreet = () => { + let localName = name; + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue) => { + setResult(resultValue); + }).catch((err) => { + console.log(err); + }); + } + + useEffect(() => { + Events.On('time', (timeValue) => { + setTime(timeValue.data); + }); + // Reload WML so it picks up the wml tags + WML.Reload(); + }, []); + + return ( +
+ +

Wails + React

+
{result}
+
+
+ setName(e.target.value)} type="text" autoComplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time}

+
+
+ ) +} + +export default App diff --git a/v3/internal/templates/react/frontend/src/main.jsx b/v3/internal/templates/react/frontend/src/main.jsx new file mode 100644 index 000000000..1943cc824 --- /dev/null +++ b/v3/internal/templates/react/frontend/src/main.jsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + , +) diff --git a/v3/internal/templates/react/frontend/vite.config.js b/v3/internal/templates/react/frontend/vite.config.js new file mode 100644 index 000000000..5a33944a9 --- /dev/null +++ b/v3/internal/templates/react/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/v3/internal/templates/react/template.json b/v3/internal/templates/react/template.json new file mode 100644 index 000000000..c9fff4a20 --- /dev/null +++ b/v3/internal/templates/react/template.json @@ -0,0 +1,9 @@ +{ + "name": "React + Vite", + "shortname": "react", + "author": "Lea Anthony", + "description": "React + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/solid-ts/frontend/.gitignore b/v3/internal/templates/solid-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/solid-ts/frontend/index.html b/v3/internal/templates/solid-ts/frontend/index.html new file mode 100644 index 000000000..86dfcc88e --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Vite + Solid + TS + + +
+ + + diff --git a/v3/internal/templates/solid-ts/frontend/package.json b/v3/internal/templates/solid-ts/frontend/package.json new file mode 100644 index 000000000..741674ea7 --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/package.json @@ -0,0 +1,21 @@ +{ + "name": "solid-ts", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "solid-js": "^1.8.7" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^5.0.8", + "vite-plugin-solid": "^2.8.0" + } +} diff --git a/v3/internal/templates/solid-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/solid-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/solid-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/solid-ts/frontend/public/solid.svg b/v3/internal/templates/solid-ts/frontend/public/solid.svg new file mode 100644 index 000000000..025aa303c --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/public/solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/solid-ts/frontend/public/style.css b/v3/internal/templates/solid-ts/frontend/public/style.css new file mode 100644 index 000000000..892241249 --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.solid:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/solid-ts/frontend/public/wails.png b/v3/internal/templates/solid-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/solid-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/solid-ts/frontend/src/App.tsx b/v3/internal/templates/solid-ts/frontend/src/App.tsx new file mode 100644 index 000000000..5027e8c1d --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/src/App.tsx @@ -0,0 +1,54 @@ +import { createSignal, onMount } from 'solid-js' +import {GreetService} from "../bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +function App() { + const [name, setName] = createSignal(''); + const [result, setResult] = createSignal('Please enter your name below 👇'); + const [time, setTime] = createSignal('Listening for Time event...'); + + const doGreet = () => { + let localName = name(); + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue: string) => { + setResult(resultValue); + }).catch((err: any) => { + console.log(err); + }); + } + + onMount(() => { + Events.On('time', (timeValue: any) => { + setTime(timeValue.data); + }); + }); + + return ( +
+ +

Wails + Solid

+
{result()}
+
+
+ setName(e.currentTarget.value)} type="text" autocomplete="off"/> + +
+
+ +
+ ) +} + +export default App diff --git a/v3/internal/templates/solid-ts/frontend/src/index.tsx b/v3/internal/templates/solid-ts/frontend/src/index.tsx new file mode 100644 index 000000000..ff5c09ee3 --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/src/index.tsx @@ -0,0 +1,7 @@ +/* @refresh reload */ +import { render } from 'solid-js/web' +import App from './App' + +const root = document.getElementById('root') + +render(() => , root!) diff --git a/v3/internal/templates/solid-ts/frontend/tsconfig.json b/v3/internal/templates/solid-ts/frontend/tsconfig.json new file mode 100644 index 000000000..7fa3d5d20 --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/solid-ts/frontend/tsconfig.node.json b/v3/internal/templates/solid-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/solid-ts/frontend/vite.config.ts b/v3/internal/templates/solid-ts/frontend/vite.config.ts new file mode 100644 index 000000000..4095d9be5 --- /dev/null +++ b/v3/internal/templates/solid-ts/frontend/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import solid from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solid()], +}) diff --git a/v3/internal/templates/solid-ts/template.json b/v3/internal/templates/solid-ts/template.json new file mode 100644 index 000000000..cc226b409 --- /dev/null +++ b/v3/internal/templates/solid-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Solid + Vite (Typescript)", + "shortname": "solid-ts", + "author": "Lea Anthony", + "description": "Solid + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/solid/frontend/.gitignore b/v3/internal/templates/solid/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/solid/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/solid/frontend/index.html b/v3/internal/templates/solid/frontend/index.html new file mode 100644 index 000000000..b074274aa --- /dev/null +++ b/v3/internal/templates/solid/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Vite + Solid + + +
+ + + diff --git a/v3/internal/templates/solid/frontend/package.json b/v3/internal/templates/solid/frontend/package.json new file mode 100644 index 000000000..03ed9141f --- /dev/null +++ b/v3/internal/templates/solid/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "solid-latest", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "solid-js": "^1.8.7" + }, + "devDependencies": { + "vite": "^5.0.8", + "vite-plugin-solid": "^2.8.0" + } +} diff --git a/v3/internal/templates/solid/frontend/public/Inter-Medium.ttf b/v3/internal/templates/solid/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/solid/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/solid/frontend/public/solid.svg b/v3/internal/templates/solid/frontend/public/solid.svg new file mode 100644 index 000000000..025aa303c --- /dev/null +++ b/v3/internal/templates/solid/frontend/public/solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/solid/frontend/public/style.css b/v3/internal/templates/solid/frontend/public/style.css new file mode 100644 index 000000000..892241249 --- /dev/null +++ b/v3/internal/templates/solid/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.solid:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/solid/frontend/public/wails.png b/v3/internal/templates/solid/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/solid/frontend/public/wails.png differ diff --git a/v3/internal/templates/solid/frontend/src/App.jsx b/v3/internal/templates/solid/frontend/src/App.jsx new file mode 100644 index 000000000..49a658928 --- /dev/null +++ b/v3/internal/templates/solid/frontend/src/App.jsx @@ -0,0 +1,54 @@ +import { createSignal, onMount } from 'solid-js' +import {GreetService} from "../bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +function App() { + const [name, setName] = createSignal(''); + const [result, setResult] = createSignal('Please enter your name below 👇'); + const [time, setTime] = createSignal('Listening for Time event...'); + + const doGreet = () => { + let localName = name(); + if (!localName) { + localName = 'anonymous'; + } + GreetService.Greet(localName).then((resultValue) => { + setResult(resultValue); + }).catch((err) => { + console.log(err); + }); + } + + onMount(() => { + Events.On('time', (timeValue) => { + setTime(timeValue.data); + }); + }); + + return ( +
+ +

Wails + Solid

+
{result()}
+
+
+ setName(e.target.value)} type="text" autocomplete="off"/> + +
+
+
+

Click on the Wails logo to learn more

+

{time()}

+
+
+ ) +} + +export default App diff --git a/v3/internal/templates/solid/frontend/src/index.jsx b/v3/internal/templates/solid/frontend/src/index.jsx new file mode 100644 index 000000000..fd6808055 --- /dev/null +++ b/v3/internal/templates/solid/frontend/src/index.jsx @@ -0,0 +1,8 @@ +/* @refresh reload */ +import { render } from 'solid-js/web' + +import App from './App' + +const root = document.getElementById('root') + +render(() => , root) diff --git a/v3/internal/templates/solid/frontend/vite.config.js b/v3/internal/templates/solid/frontend/vite.config.js new file mode 100644 index 000000000..4095d9be5 --- /dev/null +++ b/v3/internal/templates/solid/frontend/vite.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import solid from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solid()], +}) diff --git a/v3/internal/templates/solid/template.json b/v3/internal/templates/solid/template.json new file mode 100644 index 000000000..87591ef3d --- /dev/null +++ b/v3/internal/templates/solid/template.json @@ -0,0 +1,9 @@ +{ + "name": "Solid + Vite", + "shortname": "solid", + "author": "Lea Anthony", + "description": "Solid + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/svelte-ts/frontend/.gitignore b/v3/internal/templates/svelte-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/svelte-ts/frontend/.vscode/extensions.json b/v3/internal/templates/svelte-ts/frontend/.vscode/extensions.json new file mode 100644 index 000000000..bdef82015 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/v3/internal/templates/svelte-ts/frontend/index.html b/v3/internal/templates/svelte-ts/frontend/index.html new file mode 100644 index 000000000..02e1fd7d1 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + Svelte + TS + + +
+ + + diff --git a/v3/internal/templates/svelte-ts/frontend/package.json b/v3/internal/templates/svelte-ts/frontend/package.json new file mode 100644 index 000000000..c14954e77 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/package.json @@ -0,0 +1,25 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.1", + "@tsconfig/svelte": "^5.0.2", + "svelte": "^4.2.8", + "svelte-check": "^3.6.2", + "tslib": "^2.6.2", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/svelte-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/svelte-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/svelte-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/svelte-ts/frontend/public/style.css b/v3/internal/templates/svelte-ts/frontend/public/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/svelte-ts/frontend/public/svelte.svg b/v3/internal/templates/svelte-ts/frontend/public/svelte.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/public/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/svelte-ts/frontend/public/wails.png b/v3/internal/templates/svelte-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/svelte-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/svelte-ts/frontend/src/App.svelte b/v3/internal/templates/svelte-ts/frontend/src/App.svelte new file mode 100644 index 000000000..2a73938b0 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/src/App.svelte @@ -0,0 +1,51 @@ + + +
+
+ + + + + + +
+

Wails + Svelte

+
{result}
+
+
+ + +
+
+ +
+ + diff --git a/v3/internal/templates/svelte-ts/frontend/src/main.ts b/v3/internal/templates/svelte-ts/frontend/src/main.ts new file mode 100644 index 000000000..fb363569d --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/src/main.ts @@ -0,0 +1,7 @@ +import App from './App.svelte' + +const app = new App({ + target: document.getElementById('app'), +}) + +export default app diff --git a/v3/internal/templates/svelte-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/svelte-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..4078e7476 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/v3/internal/templates/svelte-ts/frontend/svelte.config.js b/v3/internal/templates/svelte-ts/frontend/svelte.config.js new file mode 100644 index 000000000..b0683fd24 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/svelte.config.js @@ -0,0 +1,7 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), +} diff --git a/v3/internal/templates/svelte-ts/frontend/tsconfig.json b/v3/internal/templates/svelte-ts/frontend/tsconfig.json new file mode 100644 index 000000000..5fb548f2b --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable checkJs if you'd like to use dynamic types in JS. + * Note that setting allowJs false does not prevent the use + * of JS in `.svelte` files. + */ + "allowJs": true, + "checkJs": true, + "isolatedModules": true + }, + "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/svelte-ts/frontend/tsconfig.node.json b/v3/internal/templates/svelte-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..494bfe083 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler" + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/svelte-ts/frontend/vite.config.ts b/v3/internal/templates/svelte-ts/frontend/vite.config.ts new file mode 100644 index 000000000..d70196943 --- /dev/null +++ b/v3/internal/templates/svelte-ts/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()], +}) diff --git a/v3/internal/templates/svelte-ts/template.json b/v3/internal/templates/svelte-ts/template.json new file mode 100644 index 000000000..597ae44db --- /dev/null +++ b/v3/internal/templates/svelte-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Svelte + Vite (Typescript)", + "shortname": "svelte-ts", + "author": "Lea Anthony", + "description": "Svelte + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/svelte/frontend/.gitignore b/v3/internal/templates/svelte/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/svelte/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/svelte/frontend/.vscode/extensions.json b/v3/internal/templates/svelte/frontend/.vscode/extensions.json new file mode 100644 index 000000000..bdef82015 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/v3/internal/templates/svelte/frontend/README.md b/v3/internal/templates/svelte/frontend/README.md new file mode 100644 index 000000000..fd6a7082f --- /dev/null +++ b/v3/internal/templates/svelte/frontend/README.md @@ -0,0 +1 @@ +# Wails + Svelte \ No newline at end of file diff --git a/v3/internal/templates/svelte/frontend/index.html b/v3/internal/templates/svelte/frontend/index.html new file mode 100644 index 000000000..20f11c0c0 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + Svelte + + +
+ + + diff --git a/v3/internal/templates/svelte/frontend/jsconfig.json b/v3/internal/templates/svelte/frontend/jsconfig.json new file mode 100644 index 000000000..e596c5823 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/jsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "moduleResolution": "Node", + "target": "ESNext", + "module": "ESNext", + /** + * svelte-preprocess cannot figure out whether you have + * a value or a type, so tell TypeScript to enforce using + * `import type` instead of `import` for Types. + */ + "importsNotUsedAsValues": "error", + "isolatedModules": true, + "resolveJsonModule": true, + /** + * To have warnings / errors of the Svelte compiler at the + * correct position, enable source maps by default. + */ + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable this if you'd like to use dynamic types. + */ + "checkJs": true + }, + /** + * Use global.d.ts instead of compilerOptions.types + * to avoid limiting type declarations. + */ + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] +} diff --git a/v3/internal/templates/svelte/frontend/package.json b/v3/internal/templates/svelte/frontend/package.json new file mode 100644 index 000000000..4655c0e95 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.1", + "svelte": "^4.2.8", + "vite": "^5.0.8" + } +} diff --git a/v3/internal/templates/svelte/frontend/public/Inter-Medium.ttf b/v3/internal/templates/svelte/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/svelte/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/svelte/frontend/public/style.css b/v3/internal/templates/svelte/frontend/public/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/svelte/frontend/public/svelte.svg b/v3/internal/templates/svelte/frontend/public/svelte.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/internal/templates/svelte/frontend/public/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/svelte/frontend/public/wails.png b/v3/internal/templates/svelte/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/svelte/frontend/public/wails.png differ diff --git a/v3/internal/templates/svelte/frontend/src/App.svelte b/v3/internal/templates/svelte/frontend/src/App.svelte new file mode 100644 index 000000000..309cc79d5 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/src/App.svelte @@ -0,0 +1,51 @@ + + +
+
+ + + + + + +
+

Wails + Svelte

+
{result}
+
+
+ + +
+
+ +
+ + diff --git a/v3/internal/templates/svelte/frontend/src/main.js b/v3/internal/templates/svelte/frontend/src/main.js new file mode 100644 index 000000000..fb363569d --- /dev/null +++ b/v3/internal/templates/svelte/frontend/src/main.js @@ -0,0 +1,7 @@ +import App from './App.svelte' + +const app = new App({ + target: document.getElementById('app'), +}) + +export default app diff --git a/v3/internal/templates/svelte/frontend/src/vite-env.d.ts b/v3/internal/templates/svelte/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..4078e7476 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/v3/internal/templates/svelte/frontend/vite.config.js b/v3/internal/templates/svelte/frontend/vite.config.js new file mode 100644 index 000000000..d70196943 --- /dev/null +++ b/v3/internal/templates/svelte/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()], +}) diff --git a/v3/internal/templates/svelte/template.json b/v3/internal/templates/svelte/template.json new file mode 100644 index 000000000..2a3da7371 --- /dev/null +++ b/v3/internal/templates/svelte/template.json @@ -0,0 +1,9 @@ +{ + "name": "Svelte + Vite", + "shortname": "svelte", + "author": "Lea Anthony", + "description": "Svelte + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/sveltekit-ts/frontend/.gitignore b/v3/internal/templates/sveltekit-ts/frontend/.gitignore new file mode 100644 index 000000000..79518f716 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/.gitignore @@ -0,0 +1,21 @@ +node_modules + +# Output +.output +.vercel +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/v3/internal/templates/sveltekit-ts/frontend/.npmrc b/v3/internal/templates/sveltekit-ts/frontend/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/v3/internal/templates/sveltekit-ts/frontend/README.md b/v3/internal/templates/sveltekit-ts/frontend/README.md new file mode 100644 index 000000000..5ce676612 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/v3/internal/templates/sveltekit-ts/frontend/package.json b/v3/internal/templates/sveltekit-ts/frontend/package.json new file mode 100644 index 000000000..58a6b3eb7 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/package.json @@ -0,0 +1,26 @@ +{ + "name": "frontend", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "dependencies": { + "@wailsio/runtime": "^3.0.0-alpha.28" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.5", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.2.7", + "svelte-check": "^4.0.0", + "typescript": "^5.0.0", + "vite": "^5.0.3" + } +} diff --git a/v3/internal/templates/sveltekit-ts/frontend/src/app.d.ts b/v3/internal/templates/sveltekit-ts/frontend/src/app.d.ts new file mode 100644 index 000000000..743f07b2e --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/v3/internal/templates/sveltekit-ts/frontend/src/app.html b/v3/internal/templates/sveltekit-ts/frontend/src/app.html new file mode 100644 index 000000000..2db551d71 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/src/app.html @@ -0,0 +1,13 @@ + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/v3/internal/templates/sveltekit-ts/frontend/src/lib/index.ts b/v3/internal/templates/sveltekit-ts/frontend/src/lib/index.ts new file mode 100644 index 000000000..856f2b6c3 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/v3/internal/templates/sveltekit-ts/frontend/src/routes/+layout.ts b/v3/internal/templates/sveltekit-ts/frontend/src/routes/+layout.ts new file mode 100644 index 000000000..ceccaaf67 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/src/routes/+layout.ts @@ -0,0 +1,2 @@ +export const prerender = true; +export const ssr = false; diff --git a/v3/internal/templates/sveltekit-ts/frontend/src/routes/+page.svelte b/v3/internal/templates/sveltekit-ts/frontend/src/routes/+page.svelte new file mode 100644 index 000000000..0227a4e89 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/src/routes/+page.svelte @@ -0,0 +1,50 @@ + + +
+
+ + + + + + +
+

Wails + Svelte

+
{result}
+
+
+ + +
+
+ +
+ + diff --git a/v3/internal/templates/sveltekit-ts/frontend/static/Inter-Medium.ttf b/v3/internal/templates/sveltekit-ts/frontend/static/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/sveltekit-ts/frontend/static/Inter-Medium.ttf differ diff --git a/v3/internal/templates/sveltekit-ts/frontend/static/favicon.png b/v3/internal/templates/sveltekit-ts/frontend/static/favicon.png new file mode 100644 index 000000000..825b9e65a Binary files /dev/null and b/v3/internal/templates/sveltekit-ts/frontend/static/favicon.png differ diff --git a/v3/internal/templates/sveltekit-ts/frontend/static/style.css b/v3/internal/templates/sveltekit-ts/frontend/static/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/static/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/sveltekit-ts/frontend/static/svelte.svg b/v3/internal/templates/sveltekit-ts/frontend/static/svelte.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/static/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/sveltekit-ts/frontend/static/wails.png b/v3/internal/templates/sveltekit-ts/frontend/static/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/sveltekit-ts/frontend/static/wails.png differ diff --git a/v3/internal/templates/sveltekit-ts/frontend/svelte.config.js b/v3/internal/templates/sveltekit-ts/frontend/svelte.config.js new file mode 100644 index 000000000..7acd75386 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/svelte.config.js @@ -0,0 +1,21 @@ +import adapter from '@sveltejs/adapter-static'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter({ + pages: 'dist', + assets: 'dist', + fallback: undefined, + precompress: false, + strict: true + }) + } +}; + +export default config; diff --git a/v3/internal/templates/sveltekit-ts/frontend/tsconfig.json b/v3/internal/templates/sveltekit-ts/frontend/tsconfig.json new file mode 100644 index 000000000..2de4494e1 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "allowUnusedLabels": true, + "noUnusedLocals": false, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/v3/internal/templates/sveltekit-ts/frontend/vite.config.ts b/v3/internal/templates/sveltekit-ts/frontend/vite.config.ts new file mode 100644 index 000000000..e2a531f50 --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/frontend/vite.config.ts @@ -0,0 +1,16 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig, searchForWorkspaceRoot } from 'vite'; + +export default defineConfig({ + server: { + fs: { + allow: [ + // search up for workspace root + searchForWorkspaceRoot(process.cwd()), + // your custom rules + './bindings/*', + ], + }, + }, + plugins: [sveltekit()] +}); diff --git a/v3/internal/templates/sveltekit-ts/template.json b/v3/internal/templates/sveltekit-ts/template.json new file mode 100644 index 000000000..f4fab015d --- /dev/null +++ b/v3/internal/templates/sveltekit-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "SvelteKit Typescript + Vite", + "shortname": "sveltekit-ts", + "author": "Atterpac", + "description": "SvelteKit + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} diff --git a/v3/internal/templates/sveltekit/frontend/.gitignore b/v3/internal/templates/sveltekit/frontend/.gitignore new file mode 100644 index 000000000..79518f716 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/.gitignore @@ -0,0 +1,21 @@ +node_modules + +# Output +.output +.vercel +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/v3/internal/templates/sveltekit/frontend/.npmrc b/v3/internal/templates/sveltekit/frontend/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/v3/internal/templates/sveltekit/frontend/README.md b/v3/internal/templates/sveltekit/frontend/README.md new file mode 100644 index 000000000..5ce676612 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/v3/internal/templates/sveltekit/frontend/frontend/Inter-Medium.ttf b/v3/internal/templates/sveltekit/frontend/frontend/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/sveltekit/frontend/frontend/Inter-Medium.ttf differ diff --git a/v3/internal/templates/sveltekit/frontend/frontend/style.css b/v3/internal/templates/sveltekit/frontend/frontend/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/frontend/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/sveltekit/frontend/frontend/svelte.svg b/v3/internal/templates/sveltekit/frontend/frontend/svelte.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/frontend/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/sveltekit/frontend/frontend/wails.png b/v3/internal/templates/sveltekit/frontend/frontend/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/sveltekit/frontend/frontend/wails.png differ diff --git a/v3/internal/templates/sveltekit/frontend/package.json b/v3/internal/templates/sveltekit/frontend/package.json new file mode 100644 index 000000000..54d96b52c --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/package.json @@ -0,0 +1,24 @@ +{ + "name": "frontend", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch" + }, + "dependencies": { + "@wailsio/runtime": "^3.0.0-alpha.28" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.5", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.2.7", + "vite": "^5.0.3" + } +} diff --git a/v3/internal/templates/sveltekit/frontend/src/app.html b/v3/internal/templates/sveltekit/frontend/src/app.html new file mode 100644 index 000000000..d72bd3485 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/src/app.html @@ -0,0 +1,13 @@ + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/v3/internal/templates/sveltekit/frontend/src/lib/index.js b/v3/internal/templates/sveltekit/frontend/src/lib/index.js new file mode 100644 index 000000000..856f2b6c3 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/src/lib/index.js @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/v3/internal/templates/sveltekit/frontend/src/routes/+layout.js b/v3/internal/templates/sveltekit/frontend/src/routes/+layout.js new file mode 100644 index 000000000..ceccaaf67 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/src/routes/+layout.js @@ -0,0 +1,2 @@ +export const prerender = true; +export const ssr = false; diff --git a/v3/internal/templates/sveltekit/frontend/src/routes/+page.svelte b/v3/internal/templates/sveltekit/frontend/src/routes/+page.svelte new file mode 100644 index 000000000..0227a4e89 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/src/routes/+page.svelte @@ -0,0 +1,50 @@ + + +
+
+ + + + + + +
+

Wails + Svelte

+
{result}
+
+
+ + +
+
+ +
+ + diff --git a/v3/internal/templates/sveltekit/frontend/static/Inter-Medium.ttf b/v3/internal/templates/sveltekit/frontend/static/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/sveltekit/frontend/static/Inter-Medium.ttf differ diff --git a/v3/internal/templates/sveltekit/frontend/static/style.css b/v3/internal/templates/sveltekit/frontend/static/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/static/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/sveltekit/frontend/static/svelte.svg b/v3/internal/templates/sveltekit/frontend/static/svelte.svg new file mode 100644 index 000000000..c5e08481f --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/static/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/sveltekit/frontend/static/wails.png b/v3/internal/templates/sveltekit/frontend/static/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/sveltekit/frontend/static/wails.png differ diff --git a/v3/internal/templates/sveltekit/frontend/svelte.config.js b/v3/internal/templates/sveltekit/frontend/svelte.config.js new file mode 100644 index 000000000..7f2998d89 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/svelte.config.js @@ -0,0 +1,19 @@ +import adapter from '@sveltejs/adapter-static'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter({ + pages: 'dist', + assets: 'dist', + fallback: undefined, + precompress: false, + strict: true + }) + } +}; + +export default config; diff --git a/v3/internal/templates/sveltekit/frontend/vite.config.js b/v3/internal/templates/sveltekit/frontend/vite.config.js new file mode 100644 index 000000000..e2a531f50 --- /dev/null +++ b/v3/internal/templates/sveltekit/frontend/vite.config.js @@ -0,0 +1,16 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig, searchForWorkspaceRoot } from 'vite'; + +export default defineConfig({ + server: { + fs: { + allow: [ + // search up for workspace root + searchForWorkspaceRoot(process.cwd()), + // your custom rules + './bindings/*', + ], + }, + }, + plugins: [sveltekit()] +}); diff --git a/v3/internal/templates/sveltekit/template.json b/v3/internal/templates/sveltekit/template.json new file mode 100644 index 000000000..f81f2813b --- /dev/null +++ b/v3/internal/templates/sveltekit/template.json @@ -0,0 +1,9 @@ +{ + "name": "SvelteKit Typescript + Vite", + "shortname": "svelte-ts", + "author": "Atterpac", + "description": "SvelteKit + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} diff --git a/v3/internal/templates/templates.go b/v3/internal/templates/templates.go new file mode 100644 index 000000000..9c96655d9 --- /dev/null +++ b/v3/internal/templates/templates.go @@ -0,0 +1,483 @@ +package templates + +import ( + "embed" + "encoding/json" + "fmt" + "io/fs" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/wailsapp/wails/v3/internal/buildinfo" + "github.com/wailsapp/wails/v3/internal/s" + "github.com/wailsapp/wails/v3/internal/version" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/pkg/errors" + "github.com/pterm/pterm" + "github.com/wailsapp/wails/v3/internal/debug" + + "github.com/wailsapp/wails/v3/internal/flags" + + "github.com/leaanthony/gosod" + + "github.com/samber/lo" +) + +//go:embed * +var templates embed.FS + +type TemplateData struct { + Name string + Description string + FS fs.FS +} + +var defaultTemplates = []TemplateData{} + +func init() { + dirs, err := templates.ReadDir(".") + if err != nil { + return + } + for _, dir := range dirs { + if strings.HasPrefix(dir.Name(), "_") { + continue + } + if dir.IsDir() { + template, err := parseTemplate(templates, dir.Name()) + if err != nil { + continue + } + defaultTemplates = append(defaultTemplates, + TemplateData{ + Name: dir.Name(), + Description: template.Description, + FS: templates, + }) + } + } +} + +func ValidTemplateName(name string) bool { + return lo.ContainsBy(defaultTemplates, func(template TemplateData) bool { + return template.Name == name + }) +} + +func GetDefaultTemplates() []TemplateData { + return defaultTemplates +} + +type TemplateOptions struct { + *flags.Init + LocalModulePath string + UseTypescript bool + WailsVersion string +} + +func getInternalTemplate(templateName string) (*Template, error) { + templateData, found := lo.Find(defaultTemplates, func(template TemplateData) bool { + return template.Name == templateName + }) + + if !found { + return nil, nil + } + + template, err := parseTemplate(templateData.FS, templateData.Name) + if err != nil { + return nil, err + } + template.source = sourceInternal + return &template, nil +} + +func getLocalTemplate(templateName string) (*Template, error) { + var template Template + var err error + _, err = os.Stat(templateName) + if err != nil { + return nil, nil + } + + template, err = parseTemplate(os.DirFS(templateName), "") + if err != nil { + println("err2 = ", err.Error()) + return nil, err + } + template.source = sourceLocal + + return &template, nil +} + +type BaseTemplate struct { + Name string `json:"name" description:"The name of the template"` + ShortName string `json:"shortname" description:"The short name of the template"` + Author string `json:"author" description:"The author of the template"` + Description string `json:"description" description:"The template description"` + HelpURL string `json:"helpurl" description:"The help url for the template"` + Version string `json:"version" description:"The version of the template" default:"v0.0.1"` + Dir string `json:"-" description:"The directory to generate the template" default:"."` + Frontend string `json:"-" description:"The frontend directory to migrate"` +} + +type source int + +const ( + sourceInternal source = 1 + sourceLocal source = 2 + sourceRemote source = 3 +) + +// Template holds data relating to a template including the metadata stored in template.yaml +type Template struct { + BaseTemplate + Schema uint8 `json:"schema"` + + // Other data + FS fs.FS `json:"-"` + source source + tempDir string +} + +func parseTemplate(template fs.FS, templateName string) (Template, error) { + var result Template + jsonFile := "template.json" + if templateName != "" { + jsonFile = templateName + "/template.json" + } + data, err := fs.ReadFile(template, jsonFile) + if err != nil { + return result, errors.Wrap(err, "Error parsing template") + } + err = json.Unmarshal(data, &result) + if err != nil { + return result, err + } + result.FS = template + + // We need to do a version check here + if result.Schema == 0 { + return result, fmt.Errorf("template not supported by wails 3. This template is probably for wails 2") + } + if result.Schema != 3 { + return result, fmt.Errorf("template version %s is not supported by wails 3. Ensure 'version' is set to 3 in the `template.json` file", result.Version) + } + + return result, nil +} + +// Clones the given uri and returns the temporary cloned directory +func gitclone(uri string) (string, error) { + // Create temporary directory + dirname, err := os.MkdirTemp("", "wails-template-*") + if err != nil { + return "", err + } + + // Parse remote template url and version number + templateInfo := strings.Split(uri, "@") + cloneOption := &git.CloneOptions{ + URL: templateInfo[0], + } + if len(templateInfo) > 1 { + cloneOption.ReferenceName = plumbing.NewTagReferenceName(templateInfo[1]) + } + + _, err = git.PlainClone(dirname, false, cloneOption) + + return dirname, err + +} + +func getRemoteTemplate(uri string) (*Template, error) { + // git clone to temporary dir + var tempDir string + tempDir, err := gitclone(uri) + + if err != nil { + return nil, err + } + // Remove the .git directory + err = os.RemoveAll(filepath.Join(tempDir, ".git")) + if err != nil { + return nil, err + } + + templateFS := os.DirFS(tempDir) + var parsedTemplate Template + parsedTemplate, err = parseTemplate(templateFS, "") + if err != nil { + return nil, err + } + parsedTemplate.tempDir = tempDir + parsedTemplate.source = sourceRemote + return &parsedTemplate, nil +} + +func Install(options *flags.Init) error { + var wd = lo.Must(os.Getwd()) + var projectDir string + if options.ProjectDir == "." || options.ProjectDir == "" { + projectDir = wd + } else { + projectDir = options.ProjectDir + } + var err error + projectDir, err = filepath.Abs(filepath.Join(projectDir, options.ProjectName)) + if err != nil { + return err + } + + buildInfo, err := buildinfo.Get() + if err != nil { + return err + } + + // Calculate relative path from project directory to LocalModulePath + var localModulePath string + + // Use module path if it is set + if buildInfo.Development { + var relativePath string + // Check if the project directory and LocalModulePath are in the same drive + if filepath.VolumeName(wd) != filepath.VolumeName(debug.LocalModulePath) { + relativePath = debug.LocalModulePath + } else { + relativePath, err = filepath.Rel(projectDir, debug.LocalModulePath) + } + if err != nil { + return err + } + localModulePath = filepath.ToSlash(relativePath + "/") + } + UseTypescript := strings.HasSuffix(options.TemplateName, "-ts") + + templateData := TemplateOptions{ + Init: options, + LocalModulePath: localModulePath, + UseTypescript: UseTypescript, + WailsVersion: version.String(), + } + + defer func() { + // if `template.json` exists, remove it + _ = os.Remove(filepath.Join(templateData.ProjectDir, "template.json")) + }() + + var template *Template + + if ValidTemplateName(options.TemplateName) { + template, err = getInternalTemplate(options.TemplateName) + if err != nil { + return err + } + } else { + template, err = getLocalTemplate(options.TemplateName) + if err != nil { + return err + } + if template == nil { + template, err = getRemoteTemplate(options.TemplateName) + } + } + + if template == nil { + return fmt.Errorf("invalid template name: %s. Use -l flag to view available templates or use a valid filepath / url to a template", options.TemplateName) + } + + templateData.ProjectDir = projectDir + + // If project directory already exists and is not empty, error + if _, err := os.Stat(templateData.ProjectDir); !os.IsNotExist(err) { + // Check if the directory is empty + files := lo.Must(os.ReadDir(templateData.ProjectDir)) + if len(files) > 0 { + return fmt.Errorf("project directory '%s' already exists and is not empty", templateData.ProjectDir) + } + } + + if template.source == sourceRemote && !options.SkipWarning { + var confirmed = confirmRemote(template) + if !confirmed { + return nil + } + } + + pterm.Printf("Creating project\n") + pterm.Printf("----------------\n\n") + table := pterm.TableData{ + {"Project Name", options.ProjectName}, + {"Project Directory", filepath.FromSlash(options.ProjectDir)}, + {"Template", template.Name}, + {"Template Source", template.HelpURL}, + {"Template Version", template.Version}, + } + err = pterm.DefaultTable.WithData(table).Render() + if err != nil { + return err + } + + switch template.source { + case sourceInternal: + tfs, err := fs.Sub(template.FS, options.TemplateName) + if err != nil { + return err + } + common, err := fs.Sub(templates, "_common") + if err != nil { + return err + } + err = gosod.New(common).Extract(options.ProjectDir, templateData) + if err != nil { + return err + } + err = gosod.New(tfs).Extract(options.ProjectDir, templateData) + if err != nil { + return err + } + case sourceLocal, sourceRemote: + data := struct { + TemplateOptions + Dir string + Name string + BinaryName string + ProductName string + ProductDescription string + ProductVersion string + ProductCompany string + ProductCopyright string + ProductComments string + ProductIdentifier string + Silent bool + Typescript bool + }{ + Name: options.ProjectName, + Silent: true, + ProductCompany: options.ProductCompany, + ProductName: options.ProductName, + ProductDescription: options.ProductDescription, + ProductVersion: options.ProductVersion, + ProductIdentifier: options.ProductIdentifier, + ProductCopyright: options.ProductCopyright, + ProductComments: options.ProductComments, + Typescript: templateData.UseTypescript, + TemplateOptions: templateData, + } + // If options.ProjectDir does not exist, create it + if _, err := os.Stat(options.ProjectDir); os.IsNotExist(err) { + err = os.Mkdir(options.ProjectDir, 0755) + if err != nil { + return err + } + } + err = gosod.New(template.FS).Extract(options.ProjectDir, data) + if err != nil { + return err + } + + if template.tempDir != "" { + s.RMDIR(template.tempDir) + } + } + if !options.SkipGoModTidy { + err = goModTidy(templateData.ProjectDir) + if err != nil { + return err + } + } + + // Change to project directory + err = os.Chdir(templateData.ProjectDir) + if err != nil { + return err + } + + pterm.Printf("\nProject '%s' created successfully.\n", options.ProjectName) + + return nil + +} + +func GenerateTemplate(options *BaseTemplate) error { + if options.Name == "" { + return fmt.Errorf("please provide a template name using the -name flag") + } + + // Get current directory + baseOutputDir, err := filepath.Abs(options.Dir) + if err != nil { + return err + } + outDir := filepath.Join(baseOutputDir, options.Name) + + // Extract base files + _, filename, _, _ := runtime.Caller(0) + basePath := filepath.Join(filepath.Dir(filename), "_common") + s.COPYDIR2(basePath, outDir) + s.RMDIR(filepath.Join(outDir, "build")) + + // Copy frontend + targetFrontendPath := filepath.Join(outDir, "frontend") + sourceFrontendPath := options.Frontend + if sourceFrontendPath == "" { + sourceFrontendPath = filepath.Join(filepath.Dir(filename), "base", "frontend") + } + s.COPYDIR2(sourceFrontendPath, targetFrontendPath) + + // Copy files from relative directory ../commands/build_assets + // Get the path to THIS file + assetPath := filepath.Join(filepath.Dir(filename), "..", "commands", "build_assets") + assetdir := filepath.Join(outDir, "build") + + s.COPYDIR2(assetPath, assetdir) + + // Copy the template NEXTSTEPS.md + s.COPY(filepath.Join(filepath.Dir(filename), "base", "NEXTSTEPS.md"), filepath.Join(outDir, "NEXTSTEPS.md")) + + // Write the template.json file + templateJSON := filepath.Join(outDir, "template.json") + // Marshall + optionsJSON, err := json.MarshalIndent(&Template{ + BaseTemplate: *options, + Schema: 3, + }, "", " ") + if err != nil { + return err + } + err = os.WriteFile(templateJSON, optionsJSON, 0o755) + if err != nil { + return err + } + + fmt.Printf("Successfully generated template in %s\n", outDir) + return nil +} + +func confirmRemote(template *Template) bool { + pterm.Println(pterm.LightRed("\n--- REMOTE TEMPLATES ---")) + + // Create boxes with the title positioned differently and containing different content + pterm.Println(pterm.LightYellow("You are creating a project using a remote template.\nThe Wails project takes no responsibility for 3rd party templates.\nOnly use remote templates that you trust.")) + + result, _ := pterm.DefaultInteractiveConfirm.WithConfirmText("Are you sure you want to continue?").WithConfirmText("y").WithRejectText("n").Show() + + return result +} + +// goModTidy runs go mod tidy in the given project directory +// It returns an error if the command fails +func goModTidy(projectDir string) error { + cmd := exec.Command("go", "mod", "tidy") + cmd.Dir = projectDir + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run go mod tidy: %w\n%s", err, string(output)) + } + return nil +} diff --git a/v3/internal/templates/vanilla-ts/frontend/.gitignore b/v3/internal/templates/vanilla-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/vanilla-ts/frontend/index.html b/v3/internal/templates/vanilla-ts/frontend/index.html new file mode 100644 index 000000000..dc4570408 --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/index.html @@ -0,0 +1,35 @@ + + + + + + + + Wails App + + +
+ +

Wails + Typescript

+
Please enter your name below 👇
+
+
+ + +
+
+ +
+ + + diff --git a/v3/internal/templates/vanilla-ts/frontend/package.json b/v3/internal/templates/vanilla-ts/frontend/package.json new file mode 100644 index 000000000..b39da7ece --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^5.0.0" + } +} diff --git a/v3/internal/templates/vanilla-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/vanilla-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/vanilla-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/vanilla-ts/frontend/public/style.css b/v3/internal/templates/vanilla-ts/frontend/public/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/vanilla-ts/frontend/public/typescript.svg b/v3/internal/templates/vanilla-ts/frontend/public/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/public/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/vanilla-ts/frontend/public/wails.png b/v3/internal/templates/vanilla-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/vanilla-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/vanilla-ts/frontend/src/main.ts b/v3/internal/templates/vanilla-ts/frontend/src/main.ts new file mode 100644 index 000000000..2e82cc21f --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/src/main.ts @@ -0,0 +1,23 @@ +import {GreetService} from "../bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +const greetButton = document.getElementById('greet')! as HTMLButtonElement; +const resultElement = document.getElementById('result')! as HTMLDivElement; +const nameElement : HTMLInputElement = document.getElementById('name')! as HTMLInputElement; +const timeElement = document.getElementById('time')! as HTMLDivElement; + +greetButton.addEventListener('click', () => { + let name = (nameElement as HTMLInputElement).value + if (!name) { + name = 'anonymous'; + } + GreetService.Greet(name).then((result: string) => { + resultElement.innerText = result; + }).catch((err: Error) => { + console.log(err); + }); +}); + +Events.On('time', (time: {data: any}) => { + timeElement.innerText = time.data; +}); diff --git a/v3/internal/templates/vanilla-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/vanilla-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/internal/templates/vanilla-ts/frontend/tsconfig.json b/v3/internal/templates/vanilla-ts/frontend/tsconfig.json new file mode 100644 index 000000000..c267ecf24 --- /dev/null +++ b/v3/internal/templates/vanilla-ts/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/v3/internal/templates/vanilla-ts/template.json b/v3/internal/templates/vanilla-ts/template.json new file mode 100644 index 000000000..6d9e55107 --- /dev/null +++ b/v3/internal/templates/vanilla-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Vanilla + Vite (Typescript)", + "shortname": "vanilla-ts", + "author": "Lea Anthony", + "description": "Vanilla + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/vanilla/frontend/.gitignore b/v3/internal/templates/vanilla/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/vanilla/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/vanilla/frontend/index.html b/v3/internal/templates/vanilla/frontend/index.html new file mode 100644 index 000000000..b81d9729f --- /dev/null +++ b/v3/internal/templates/vanilla/frontend/index.html @@ -0,0 +1,35 @@ + + + + + + + + Wails App + + +
+ +

Wails + Javascript

+
+
Please enter your name below 👇
+
+ + +
+
+ +
+ + + diff --git a/v3/internal/templates/vanilla/frontend/main.js b/v3/internal/templates/vanilla/frontend/main.js new file mode 100644 index 000000000..c24b3b1ef --- /dev/null +++ b/v3/internal/templates/vanilla/frontend/main.js @@ -0,0 +1,21 @@ +import {GreetService} from "./bindings/changeme"; +import {Events} from "@wailsio/runtime"; + +const resultElement = document.getElementById('result'); +const timeElement = document.getElementById('time'); + +window.doGreet = () => { + let name = document.getElementById('name').value; + if (!name) { + name = 'anonymous'; + } + GreetService.Greet(name).then((result) => { + resultElement.innerText = result; + }).catch((err) => { + console.log(err); + }); +} + +Events.On('time', (time) => { + timeElement.innerText = time.data; +}); diff --git a/v3/internal/templates/vanilla/frontend/package.json b/v3/internal/templates/vanilla/frontend/package.json new file mode 100644 index 000000000..0a118e984 --- /dev/null +++ b/v3/internal/templates/vanilla/frontend/package.json @@ -0,0 +1,18 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "vite": "^5.0.0" + } +} diff --git a/v3/internal/templates/vanilla/frontend/public/Inter-Medium.ttf b/v3/internal/templates/vanilla/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/vanilla/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/vanilla/frontend/public/javascript.svg b/v3/internal/templates/vanilla/frontend/public/javascript.svg new file mode 100644 index 000000000..f9abb2b72 --- /dev/null +++ b/v3/internal/templates/vanilla/frontend/public/javascript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/vanilla/frontend/public/style.css b/v3/internal/templates/vanilla/frontend/public/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/internal/templates/vanilla/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/vanilla/frontend/public/wails.png b/v3/internal/templates/vanilla/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/vanilla/frontend/public/wails.png differ diff --git a/v3/internal/templates/vanilla/template.json b/v3/internal/templates/vanilla/template.json new file mode 100644 index 000000000..5f8a281a5 --- /dev/null +++ b/v3/internal/templates/vanilla/template.json @@ -0,0 +1,9 @@ +{ + "name": "Vanilla + Vite", + "shortname": "vanilla", + "author": "Lea Anthony", + "description": "Vanilla + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/vue-ts/.vscode/extensions.json b/v3/internal/templates/vue-ts/.vscode/extensions.json new file mode 100644 index 000000000..86c616f02 --- /dev/null +++ b/v3/internal/templates/vue-ts/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["octref.vetur"] +} diff --git a/v3/internal/templates/vue-ts/frontend/.gitignore b/v3/internal/templates/vue-ts/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/vue-ts/frontend/.vscode/extensions.json b/v3/internal/templates/vue-ts/frontend/.vscode/extensions.json new file mode 100644 index 000000000..c0a6e5a48 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] +} diff --git a/v3/internal/templates/vue-ts/frontend/README.md b/v3/internal/templates/vue-ts/frontend/README.md new file mode 100644 index 000000000..ef72fd524 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/README.md @@ -0,0 +1,18 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/v3/internal/templates/vue-ts/frontend/package.json b/v3/internal/templates/vue-ts/frontend/package.json new file mode 100644 index 000000000..dc08ffa11 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/package.json @@ -0,0 +1,22 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vue-tsc && vite build --minify false --mode development", + "build": "vue-tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "vue": "^3.2.45" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.0.0", + "typescript": "^4.9.3", + "vite": "^5.0.0", + "vue-tsc": "^1.0.11" + } +} diff --git a/v3/internal/templates/vue-ts/frontend/public/Inter-Medium.ttf b/v3/internal/templates/vue-ts/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/vue-ts/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/vue-ts/frontend/public/style.css b/v3/internal/templates/vue-ts/frontend/public/style.css new file mode 100644 index 000000000..187e7e4b9 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/public/style.css @@ -0,0 +1,145 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + text-align: center; +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/vue-ts/frontend/public/vue.svg b/v3/internal/templates/vue-ts/frontend/public/vue.svg new file mode 100644 index 000000000..770e9d333 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/public/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/vue-ts/frontend/public/wails.png b/v3/internal/templates/vue-ts/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/vue-ts/frontend/public/wails.png differ diff --git a/v3/internal/templates/vue-ts/frontend/src/App.vue b/v3/internal/templates/vue-ts/frontend/src/App.vue new file mode 100644 index 000000000..8f62a7e44 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/src/App.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/v3/internal/templates/vue-ts/frontend/src/components/HelloWorld.vue b/v3/internal/templates/vue-ts/frontend/src/components/HelloWorld.vue new file mode 100644 index 000000000..e73e33e23 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/src/components/HelloWorld.vue @@ -0,0 +1,47 @@ + + + diff --git a/v3/internal/templates/vue-ts/frontend/src/main.ts b/v3/internal/templates/vue-ts/frontend/src/main.ts new file mode 100644 index 000000000..01433bca2 --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/src/main.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/v3/internal/templates/vue-ts/frontend/src/vite-env.d.ts b/v3/internal/templates/vue-ts/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/internal/templates/vue-ts/frontend/tsconfig.json b/v3/internal/templates/vue-ts/frontend/tsconfig.json new file mode 100644 index 000000000..8cef1a67b --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Node", + "strict": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ESNext", "DOM"], + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/v3/internal/templates/vue-ts/frontend/tsconfig.node.json b/v3/internal/templates/vue-ts/frontend/tsconfig.node.json new file mode 100644 index 000000000..9d31e2aed --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/v3/internal/templates/vue-ts/frontend/vite.config.ts b/v3/internal/templates/vue-ts/frontend/vite.config.ts new file mode 100644 index 000000000..05c17402a --- /dev/null +++ b/v3/internal/templates/vue-ts/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/v3/internal/templates/vue-ts/template.json b/v3/internal/templates/vue-ts/template.json new file mode 100644 index 000000000..d87ef872e --- /dev/null +++ b/v3/internal/templates/vue-ts/template.json @@ -0,0 +1,9 @@ +{ + "name": "Vue + Vite (Typescript)", + "shortname": "vue-ts", + "author": "Lea Anthony", + "description": "Vue + TS + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/templates/vue/.vscode/extensions.json b/v3/internal/templates/vue/.vscode/extensions.json new file mode 100644 index 000000000..86c616f02 --- /dev/null +++ b/v3/internal/templates/vue/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["octref.vetur"] +} diff --git a/v3/internal/templates/vue/frontend/.gitignore b/v3/internal/templates/vue/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/v3/internal/templates/vue/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v3/internal/templates/vue/frontend/.vscode/extensions.json b/v3/internal/templates/vue/frontend/.vscode/extensions.json new file mode 100644 index 000000000..c0a6e5a48 --- /dev/null +++ b/v3/internal/templates/vue/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] +} diff --git a/v3/internal/templates/vue/frontend/index.html b/v3/internal/templates/vue/frontend/index.html new file mode 100644 index 000000000..2700f93b9 --- /dev/null +++ b/v3/internal/templates/vue/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Wails + Vue + + +
+ + + diff --git a/v3/internal/templates/vue/frontend/package.json b/v3/internal/templates/vue/frontend/package.json new file mode 100644 index 000000000..6958956b3 --- /dev/null +++ b/v3/internal/templates/vue/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "vite build --minify false --mode development", + "build": "vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest", + "vue": "^3.2.45" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.0.0", + "vite": "^5.0.0" + } +} diff --git a/v3/internal/templates/vue/frontend/public/Inter-Medium.ttf b/v3/internal/templates/vue/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/internal/templates/vue/frontend/public/Inter-Medium.ttf differ diff --git a/v3/internal/templates/vue/frontend/public/style.css b/v3/internal/templates/vue/frontend/public/style.css new file mode 100644 index 000000000..187e7e4b9 --- /dev/null +++ b/v3/internal/templates/vue/frontend/public/style.css @@ -0,0 +1,145 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + text-align: center; +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/internal/templates/vue/frontend/public/vue.svg b/v3/internal/templates/vue/frontend/public/vue.svg new file mode 100644 index 000000000..770e9d333 --- /dev/null +++ b/v3/internal/templates/vue/frontend/public/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/internal/templates/vue/frontend/public/wails.png b/v3/internal/templates/vue/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/internal/templates/vue/frontend/public/wails.png differ diff --git a/v3/internal/templates/vue/frontend/src/App.vue b/v3/internal/templates/vue/frontend/src/App.vue new file mode 100644 index 000000000..8af6ab35d --- /dev/null +++ b/v3/internal/templates/vue/frontend/src/App.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/v3/internal/templates/vue/frontend/src/components/HelloWorld.vue b/v3/internal/templates/vue/frontend/src/components/HelloWorld.vue new file mode 100644 index 000000000..3c7c085c5 --- /dev/null +++ b/v3/internal/templates/vue/frontend/src/components/HelloWorld.vue @@ -0,0 +1,49 @@ + + + diff --git a/v3/internal/templates/vue/frontend/src/main.js b/v3/internal/templates/vue/frontend/src/main.js new file mode 100644 index 000000000..01433bca2 --- /dev/null +++ b/v3/internal/templates/vue/frontend/src/main.js @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/v3/internal/templates/vue/frontend/vite.config.js b/v3/internal/templates/vue/frontend/vite.config.js new file mode 100644 index 000000000..05c17402a --- /dev/null +++ b/v3/internal/templates/vue/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/v3/internal/templates/vue/template.json b/v3/internal/templates/vue/template.json new file mode 100644 index 000000000..b6e8faf93 --- /dev/null +++ b/v3/internal/templates/vue/template.json @@ -0,0 +1,9 @@ +{ + "name": "Vue + Vite", + "shortname": "vue", + "author": "Lea Anthony", + "description": "Vue + Vite development server", + "helpurl": "https://wails.io", + "version": "v0.0.1", + "schema": 3 +} \ No newline at end of file diff --git a/v3/internal/term/term.go b/v3/internal/term/term.go new file mode 100644 index 000000000..9127ff08a --- /dev/null +++ b/v3/internal/term/term.go @@ -0,0 +1,127 @@ +package term + +import ( + "fmt" + "os" + + "github.com/pterm/pterm" + "github.com/wailsapp/wails/v3/internal/generator/config" + "github.com/wailsapp/wails/v3/internal/version" + "golang.org/x/term" +) + +func Header(header string) { + // Print Wails with the current version in white on red background with the header in white with a green background + pterm.BgLightRed.Print(pterm.LightWhite(" Wails (" + version.String() + ") ")) + pterm.BgLightGreen.Println(pterm.LightWhite(" " + header + " ")) +} + +func IsTerminal() bool { + return term.IsTerminal(int(os.Stdout.Fd())) && (os.Getenv("CI") != "true") +} + +type Spinner struct { + spinner *pterm.SpinnerPrinter +} + +func (s Spinner) Logger() config.Logger { + return config.DefaultPtermLogger(s.spinner) +} + +func StartSpinner(text string) Spinner { + if !IsTerminal() { + return Spinner{} + } + spinner, err := pterm.DefaultSpinner.Start(text) + if err != nil { + return Spinner{} + } + spinner.RemoveWhenDone = true + return Spinner{spinner} +} + +func StopSpinner(s Spinner) { + if s.spinner != nil { + _ = s.spinner.Stop() + } +} + +func output(input any, printer pterm.PrefixPrinter, args ...any) { + switch v := input.(type) { + case string: + printer.Printfln(input.(string), args...) + case error: + printer.Println(v.Error()) + default: + printer.Printfln("%v", v) + } +} + +func Info(input any) { + output(input, pterm.Info) +} + +func Infof(input any, args ...interface{}) { + output(input, pterm.Info, args...) +} + +func Warning(input any) { + output(input, pterm.Warning) +} + +func Warningf(input any, args ...any) { + output(input, pterm.Warning, args...) +} + +func Error(input any) { + output(input, pterm.Error) +} + +func Success(input any) { + output(input, pterm.Success) +} + +func Section(s string) { + style := pterm.NewStyle(pterm.BgDefault, pterm.FgLightBlue, pterm.Bold) + style.Println("\n# " + s + " \n") +} + +func DisableColor() { + pterm.DisableColor() +} + +func EnableOutput() { + pterm.EnableOutput() +} + +func DisableOutput() { + pterm.DisableOutput() +} + +func EnableDebug() { + pterm.EnableDebugMessages() +} + +func DisableDebug() { + pterm.DisableDebugMessages() +} + +func Println(s string) { + pterm.Println(s) +} + +func Hyperlink(url, text string) string { + // OSC 8 sequence to start a clickable link + linkStart := "\x1b]8;;" + + // OSC 8 sequence to end a clickable link + linkEnd := "\x1b]8;;\x1b\\" + + // ANSI escape code for underline + underlineStart := "\x1b[4m" + + // ANSI escape code to reset text formatting + resetFormat := "\x1b[0m" + + return fmt.Sprintf("%s%s%s%s%s%s%s", linkStart, url, "\x1b\\", underlineStart, text, resetFormat, linkEnd) +} diff --git a/v3/internal/version/version.go b/v3/internal/version/version.go new file mode 100644 index 000000000..fedd279af --- /dev/null +++ b/v3/internal/version/version.go @@ -0,0 +1,26 @@ +package version + +import ( + _ "embed" + "github.com/wailsapp/wails/v3/internal/debug" +) + +//go:embed version.txt +var versionString string + +const DevVersion = "v3.0.0-dev" + +func String() string { + if !IsDev() { + return versionString + } + return DevVersion +} + +func LatestStable() string { + return versionString +} + +func IsDev() bool { + return debug.LocalModulePath != "" +} diff --git a/v3/internal/version/version.txt b/v3/internal/version/version.txt new file mode 100644 index 000000000..d8e381a09 --- /dev/null +++ b/v3/internal/version/version.txt @@ -0,0 +1 @@ +v3.0.0-alpha.27 diff --git a/v3/pkg/application/TODO.md b/v3/pkg/application/TODO.md new file mode 100644 index 000000000..ae4700425 --- /dev/null +++ b/v3/pkg/application/TODO.md @@ -0,0 +1,10 @@ + +Features +- [ ] AssetServer +- [ ] Offline page if navigating to external URL +- [x] Application menu + +Bugs +- [ ] Resize Window +- [ ] Fullscreen/Maximise/Minimise/Restore + diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go new file mode 100644 index 000000000..f1e313a3d --- /dev/null +++ b/v3/pkg/application/application.go @@ -0,0 +1,894 @@ +package application + +import ( + "context" + "embed" + "encoding/json" + "errors" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "runtime" + "slices" + "strconv" + "strings" + "sync" + + "github.com/wailsapp/wails/v3/internal/signal" + + "github.com/wailsapp/wails/v3/internal/assetserver" + "github.com/wailsapp/wails/v3/internal/assetserver/bundledassets" + "github.com/wailsapp/wails/v3/internal/assetserver/webview" + "github.com/wailsapp/wails/v3/internal/capabilities" +) + +//go:embed assets/* +var alphaAssets embed.FS + +var globalApplication *App + +// AlphaAssets is the default assets for the alpha application +var AlphaAssets = AssetOptions{ + Handler: BundledAssetFileServer(alphaAssets), +} + +func init() { + runtime.LockOSThread() +} + +type EventListener struct { + callback func(app *ApplicationEvent) +} + +func Get() *App { + return globalApplication +} + +func New(appOptions Options) *App { + if globalApplication != nil { + return globalApplication + } + + mergeApplicationDefaults(&appOptions) + + result := newApplication(appOptions) + globalApplication = result + fatalHandler(result.handleFatalError) + + if result.Logger == nil { + if result.isDebugMode { + result.Logger = DefaultLogger(result.options.LogLevel) + } else { + result.Logger = slog.New(slog.NewTextHandler(io.Discard, nil)) + } + } + + if !appOptions.DisableDefaultSignalHandler { + result.signalHandler = signal.NewSignalHandler(result.Quit) + result.signalHandler.Logger = result.Logger + result.signalHandler.ExitMessage = func(sig os.Signal) string { + return "Quitting application..." + } + } + + result.logStartup() + result.logPlatformInfo() + + result.customEventProcessor = NewWailsEventProcessor(result.Event.dispatch) + + messageProc := NewMessageProcessor(result.Logger) + opts := &assetserver.Options{ + Handler: appOptions.Assets.Handler, + Middleware: assetserver.ChainMiddleware( + func(next http.Handler) http.Handler { + if m := appOptions.Assets.Middleware; m != nil { + return m(next) + } + return next + }, + func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + path := req.URL.Path + switch path { + case "/wails/runtime.js": + err := assetserver.ServeFile(rw, path, bundledassets.RuntimeJS) + if err != nil { + result.fatal("unable to serve runtime.js: %w", err) + } + case "/wails/runtime": + messageProc.ServeHTTP(rw, req) + case "/wails/capabilities": + err := assetserver.ServeFile( + rw, + path, + globalApplication.capabilities.AsBytes(), + ) + if err != nil { + result.fatal("unable to serve capabilities: %w", err) + } + case "/wails/flags": + updatedOptions := result.impl.GetFlags(appOptions) + flags, err := json.Marshal(updatedOptions) + if err != nil { + result.fatal("invalid flags provided to application: %w", err) + } + err = assetserver.ServeFile(rw, path, flags) + if err != nil { + result.fatal("unable to serve flags: %w", err) + } + default: + next.ServeHTTP(rw, req) + } + }) + }, + ), + Logger: result.Logger, + } + + if appOptions.Assets.DisableLogging { + opts.Logger = slog.New(slog.NewTextHandler(io.Discard, nil)) + } + + srv, err := assetserver.NewAssetServer(opts) + if err != nil { + result.fatal("application initialisation failed: %w", err) + } + + result.assets = srv + result.assets.LogDetails() + + result.bindings = NewBindings(appOptions.MarshalError, appOptions.BindAliases) + result.options.Services = slices.Clone(appOptions.Services) + + // Process keybindings + if result.options.KeyBindings != nil { + result.keyBindings = processKeyBindingOptions(result.options.KeyBindings) + } + + if appOptions.OnShutdown != nil { + result.OnShutdown(appOptions.OnShutdown) + } + + // Initialize single instance manager if enabled + if appOptions.SingleInstance != nil { + manager, err := newSingleInstanceManager(result, appOptions.SingleInstance) + if err != nil { + if errors.Is(err, alreadyRunningError) && manager != nil { + err = manager.notifyFirstInstance() + if err != nil { + globalApplication.error("failed to notify first instance: %w", err) + } + os.Exit(appOptions.SingleInstance.ExitCode) + } + result.fatal("failed to initialize single instance manager: %w", err) + } else { + result.singleInstanceManager = manager + } + } + + return result +} + +func mergeApplicationDefaults(o *Options) { + if o.Name == "" { + o.Name = "My Wails Application" + } + if o.Description == "" { + o.Description = "An application written using Wails" + } + if o.Windows.WndClass == "" { + o.Windows.WndClass = "WailsWebviewWindow" + } +} + +type ( + platformApp interface { + run() error + destroy() + setApplicationMenu(menu *Menu) + name() string + getCurrentWindowID() uint + showAboutDialog(name string, description string, icon []byte) + setIcon(icon []byte) + on(id uint) + dispatchOnMainThread(id uint) + hide() + show() + getPrimaryScreen() (*Screen, error) + getScreens() ([]*Screen, error) + GetFlags(options Options) map[string]any + isOnMainThread() bool + isDarkMode() bool + getAccentColor() string + } + + runnable interface { + Run() + } +) + +// Messages sent from javascript get routed here +type windowMessage struct { + windowId uint + message string +} + +var windowMessageBuffer = make(chan *windowMessage, 5) + +// DropZoneDetails contains information about the HTML element +// at the location of a file drop. +type DropZoneDetails struct { + X int `json:"x"` + Y int `json:"y"` + ElementID string `json:"id"` + ClassList []string `json:"classList"` + Attributes map[string]string `json:"attributes,omitempty"` +} + +type dragAndDropMessage struct { + windowId uint + filenames []string + X int + Y int + DropZone *DropZoneDetails +} + +var windowDragAndDropBuffer = make(chan *dragAndDropMessage, 5) + +func addDragAndDropMessage(windowId uint, filenames []string, dropZone *DropZoneDetails) { + windowDragAndDropBuffer <- &dragAndDropMessage{ + windowId: windowId, + filenames: filenames, + DropZone: dropZone, + } +} + +var _ webview.Request = &webViewAssetRequest{} + +const webViewRequestHeaderWindowId = "x-wails-window-id" +const webViewRequestHeaderWindowName = "x-wails-window-name" + +type webViewAssetRequest struct { + webview.Request + windowId uint + windowName string +} + +var windowKeyEvents = make(chan *windowKeyEvent, 5) + +type windowKeyEvent struct { + windowId uint + acceleratorString string +} + +func (r *webViewAssetRequest) Header() (http.Header, error) { + h, err := r.Request.Header() + if err != nil { + return nil, err + } + + hh := h.Clone() + hh.Set(webViewRequestHeaderWindowId, strconv.FormatUint(uint64(r.windowId), 10)) + return hh, nil +} + +var webviewRequests = make(chan *webViewAssetRequest, 5) + +type eventHook struct { + callback func(event *ApplicationEvent) +} + +type App struct { + ctx context.Context + cancel context.CancelFunc + options Options + applicationEventListeners map[uint][]*EventListener + applicationEventListenersLock sync.RWMutex + applicationEventHooks map[uint][]*eventHook + applicationEventHooksLock sync.RWMutex + + // Manager pattern for organized API + Window *WindowManager + ContextMenu *ContextMenuManager + KeyBinding *KeyBindingManager + Browser *BrowserManager + Env *EnvironmentManager + Dialog *DialogManager + Event *EventManager + Menu *MenuManager + Screen *ScreenManager + Clipboard *ClipboardManager + SystemTray *SystemTrayManager + + // Windows + windows map[uint]Window + windowsLock sync.RWMutex + + // System Trays + systemTrays map[uint]*SystemTray + systemTraysLock sync.Mutex + systemTrayID uint + systemTrayIDLock sync.RWMutex + + // MenuItems + menuItems map[uint]*MenuItem + menuItemsLock sync.Mutex + + // Starting and running + starting bool + running bool + runLock sync.Mutex + pendingRun []runnable + + bindings *Bindings + + // platform app + impl platformApp + + // The main application menu (private - use app.Menu.GetApplicationMenu/SetApplicationMenu) + applicationMenu *Menu + + clipboard *Clipboard + customEventProcessor *EventProcessor + Logger *slog.Logger + + contextMenus map[string]*ContextMenu + contextMenusLock sync.RWMutex + + assets *assetserver.AssetServer + startURL string + + // Hooks + windowCreatedCallbacks []func(window Window) + pid int + + // Capabilities + capabilities capabilities.Capabilities + isDebugMode bool + + // Keybindings + keyBindings map[string]func(window Window) + keyBindingsLock sync.RWMutex + + // Shutdown + performingShutdown bool + shutdownLock sync.Mutex + serviceShutdownLock sync.Mutex + + // Shutdown tasks are run when the application is shutting down. + // They are run in the order they are added and run on the main thread. + // The application option `OnShutdown` is run first. + shutdownTasks []func() + + // signalHandler is used to handle signals + signalHandler *signal.SignalHandler + + // Wails ApplicationEvent Listener related + wailsEventListenerLock sync.Mutex + wailsEventListeners []WailsEventListener + + // singleInstanceManager handles single instance functionality + singleInstanceManager *singleInstanceManager +} + +func (a *App) Config() Options { + return a.options +} + +// Context returns the application context that is canceled when the application shuts down. +// This context should be used for graceful shutdown of goroutines and long-running operations. +func (a *App) Context() context.Context { + return a.ctx +} + +func (a *App) handleWarning(msg string) { + if a.options.WarningHandler != nil { + a.options.WarningHandler(msg) + } else { + a.Logger.Warn(msg) + } +} + +func (a *App) handleError(err error) { + if a.options.ErrorHandler != nil { + a.options.ErrorHandler(err) + } else { + a.Logger.Error(err.Error()) + } +} + +// RegisterService appends the given service to the list of bound services. +// Registered services will be bound and initialised +// in registration order upon calling [App.Run]. +// +// RegisterService will log an error message +// and discard the given service +// if called after [App.Run]. +func (a *App) RegisterService(service Service) { + a.runLock.Lock() + defer a.runLock.Unlock() + + if a.starting || a.running { + a.error( + "services must be registered before running the application. Service '%s' will not be registered.", + getServiceName(service), + ) + return + } + + a.options.Services = append(a.options.Services, service) +} + +// EmitEvent will emit an event + +func (a *App) handleFatalError(err error) { + a.handleError(&FatalError{err: err}) + os.Exit(1) +} + +func (a *App) init() { + a.ctx, a.cancel = context.WithCancel(context.Background()) + a.applicationEventHooks = make(map[uint][]*eventHook) + a.applicationEventListeners = make(map[uint][]*EventListener) + a.windows = make(map[uint]Window) + a.systemTrays = make(map[uint]*SystemTray) + a.contextMenus = make(map[string]*ContextMenu) + a.keyBindings = make(map[string]func(window Window)) + a.Logger = a.options.Logger + a.pid = os.Getpid() + a.wailsEventListeners = make([]WailsEventListener, 0) + + // Initialize managers + a.Window = newWindowManager(a) + a.ContextMenu = newContextMenuManager(a) + a.KeyBinding = newKeyBindingManager(a) + a.Browser = newBrowserManager(a) + a.Env = newEnvironmentManager(a) + a.Dialog = newDialogManager(a) + a.Event = newEventManager(a) + a.Menu = newMenuManager(a) + a.Screen = newScreenManager(a) + a.Clipboard = newClipboardManager(a) + a.SystemTray = newSystemTrayManager(a) +} + +func (a *App) Capabilities() capabilities.Capabilities { + return a.capabilities +} + +//func (a *App) RegisterListener(listener WailsEventListener) { +// a.wailsEventListenerLock.Lock() +// a.wailsEventListeners = append(a.wailsEventListeners, listener) +// a.wailsEventListenerLock.Unlock() +//} +// +//func (a *App) RegisterServiceHandler(prefix string, handler http.Handler) { +// a.assets.AttachServiceHandler(prefix, handler) +//} + +func (a *App) GetPID() int { + return a.pid +} + +func (a *App) info(message string, args ...any) { + if a.Logger != nil { + go func() { + defer handlePanic() + a.Logger.Info(message, args...) + }() + } +} + +func (a *App) debug(message string, args ...any) { + if a.Logger != nil { + go func() { + defer handlePanic() + a.Logger.Debug(message, args...) + }() + } +} + +func (a *App) fatal(message string, args ...any) { + err := fmt.Errorf(message, args...) + a.handleFatalError(err) +} +func (a *App) warning(message string, args ...any) { + msg := fmt.Sprintf(message, args...) + a.handleWarning(msg) +} + +func (a *App) error(message string, args ...any) { + a.handleError(fmt.Errorf(message, args...)) +} + +func (a *App) Run() error { + a.runLock.Lock() + // Prevent double invocations. + if a.starting || a.running { + a.runLock.Unlock() + return errors.New("application is running or a previous run has failed") + } + // Block further service registrations. + a.starting = true + a.runLock.Unlock() + + // Ensure application context is canceled in case of failures. + defer a.cancel() + + // Call post-create hooks + err := a.preRun() + if err != nil { + return err + } + + a.impl = newPlatformApp(a) + + // Ensure services are shut down in case of failures. + defer a.shutdownServices() + + // Ensure application context is canceled before service shutdown (duplicate calls don't hurt). + defer a.cancel() + + // Startup services before dispatching any events. + // No need to hold the lock here because a.options.Services may only change when a.running is false. + services := a.options.Services + a.options.Services = nil + for i, service := range services { + if err := a.startupService(service); err != nil { + return fmt.Errorf("error starting service '%s': %w", getServiceName(service), err) + } + // Schedule started services for shutdown. + a.options.Services = services[:i+1] + } + + go func() { + for { + event := <-applicationEvents + go a.Event.handleApplicationEvent(event) + } + }() + go func() { + for { + event := <-windowEvents + go a.handleWindowEvent(event) + } + }() + go func() { + for { + request := <-webviewRequests + go a.handleWebViewRequest(request) + } + }() + go func() { + for { + event := <-windowMessageBuffer + go a.handleWindowMessage(event) + } + }() + go func() { + for { + event := <-windowKeyEvents + go a.handleWindowKeyEvent(event) + } + }() + go func() { + for { + dragAndDropMessage := <-windowDragAndDropBuffer + a.Logger.Debug( + "[DragDropDebug] App.Run: Received message from windowDragAndDropBuffer", + "message", + fmt.Sprintf("%+v", dragAndDropMessage), + ) + go a.handleDragAndDropMessage(dragAndDropMessage) + } + }() + + go func() { + for { + menuItemID := <-menuItemClicked + go a.Menu.handleMenuItemClicked(menuItemID) + } + }() + + a.runLock.Lock() + a.running = true + a.runLock.Unlock() + + // No need to hold the lock here because + // - a.pendingRun may only change while a.running is false. + // - runnables are scheduled asynchronously anyway. + for _, pending := range a.pendingRun { + go func() { + defer handlePanic() + pending.Run() + }() + } + a.pendingRun = nil + + // set the application menu + if runtime.GOOS == "darwin" { + a.impl.setApplicationMenu(a.applicationMenu) + } + if a.options.Icon != nil { + a.impl.setIcon(a.options.Icon) + } + + return a.impl.run() +} + +func (a *App) startupService(service Service) error { + err := a.bindings.Add(service) + if err != nil { + return fmt.Errorf("cannot bind service methods: %w", err) + } + + if service.options.Route != "" { + handler, ok := service.Instance().(http.Handler) + if !ok { + handler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + http.Error( + rw, + fmt.Sprintf( + "Service '%s' does not handle HTTP requests", + getServiceName(service), + ), + http.StatusServiceUnavailable, + ) + }) + } + a.assets.AttachServiceHandler(service.options.Route, handler) + } + + if s, ok := service.instance.(ServiceStartup); ok { + a.debug("Starting up service:", "name", getServiceName(service)) + return s.ServiceStartup(a.ctx, service.options) + } + + return nil +} + +func (a *App) shutdownServices() { + // Acquire lock to prevent double calls (defer in Run() + OnShutdown) + a.serviceShutdownLock.Lock() + defer a.serviceShutdownLock.Unlock() + + // Ensure app context is canceled first (duplicate calls don't hurt). + a.cancel() + + for len(a.options.Services) > 0 { + last := len(a.options.Services) - 1 + service := a.options.Services[last] + a.options.Services = a.options.Services[:last] // Prevent double shutdowns + + if s, ok := service.instance.(ServiceShutdown); ok { + a.debug("Shutting down service:", "name", getServiceName(service)) + if err := s.ServiceShutdown(); err != nil { + a.error("error shutting down service '%s': %w", getServiceName(service), err) + } + } + } +} + +func (a *App) handleDragAndDropMessage(event *dragAndDropMessage) { + a.Logger.Debug( + "[DragDropDebug] App.handleDragAndDropMessage: Called with event", + "event", + fmt.Sprintf("%+v", event), + ) + defer handlePanic() + // Get window from window map + a.windowsLock.Lock() + window, ok := a.windows[event.windowId] + a.windowsLock.Unlock() + if !ok { + a.warning("WebviewWindow #%d not found", event.windowId) + return + } + // Get callback from window + a.Logger.Debug( + "[DragDropDebug] App.handleDragAndDropMessage: Calling window.HandleDragAndDropMessage", + "windowID", + event.windowId, + ) + window.HandleDragAndDropMessage(event.filenames, event.DropZone) +} + +func (a *App) handleWindowMessage(event *windowMessage) { + defer handlePanic() + // Get window from window map + a.windowsLock.RLock() + window, ok := a.windows[event.windowId] + a.windowsLock.RUnlock() + if !ok { + a.warning("WebviewWindow #%d not found", event.windowId) + return + } + // Check if the message starts with "wails:" + if strings.HasPrefix(event.message, "wails:") { + window.HandleMessage(event.message) + } else { + if a.options.RawMessageHandler != nil { + a.options.RawMessageHandler(window, event.message) + } + } +} + +func (a *App) handleWebViewRequest(request *webViewAssetRequest) { + defer handlePanic() + a.assets.ServeWebViewRequest(request) +} + +func (a *App) handleWindowEvent(event *windowEvent) { + defer handlePanic() + // Get window from window map + a.windowsLock.RLock() + window, ok := a.windows[event.WindowID] + a.windowsLock.RUnlock() + if !ok { + a.warning("Window #%d not found", event.WindowID) + return + } + window.HandleWindowEvent(event.EventID) +} + +// OnShutdown adds a function to be run when the application is shutting down. +func (a *App) OnShutdown(f func()) { + if f == nil { + return + } + + a.shutdownLock.Lock() + + if !a.performingShutdown { + defer a.shutdownLock.Unlock() + a.shutdownTasks = append(a.shutdownTasks, f) + return + } + + a.shutdownLock.Unlock() + InvokeAsync(f) +} + +func (a *App) cleanup() { + a.shutdownLock.Lock() + if a.performingShutdown { + a.shutdownLock.Unlock() + return + } + a.cancel() // Cancel app context before running shutdown hooks. + a.performingShutdown = true + a.shutdownLock.Unlock() + + // No need to hold the lock here because a.shutdownTasks + // may only change while a.performingShutdown is false. + for _, shutdownTask := range a.shutdownTasks { + InvokeSync(shutdownTask) + } + InvokeSync(func() { + a.shutdownServices() + a.windowsLock.RLock() + for _, window := range a.windows { + window.Close() + } + a.windows = nil + a.windowsLock.RUnlock() + a.systemTraysLock.Lock() + for _, systray := range a.systemTrays { + systray.destroy() + } + a.systemTrays = nil + a.systemTraysLock.Unlock() + + // Cleanup single instance manager + if a.singleInstanceManager != nil { + a.singleInstanceManager.cleanup() + } + + a.postQuit() + + if a.options.PostShutdown != nil { + a.options.PostShutdown() + } + }) +} + +func (a *App) Quit() { + if a.impl != nil { + InvokeSync(a.impl.destroy) + } +} + +func (a *App) SetIcon(icon []byte) { + if a.impl != nil { + a.impl.setIcon(icon) + } +} + +func InfoDialog() *MessageDialog { + return newMessageDialog(InfoDialogType) +} + +func QuestionDialog() *MessageDialog { + return newMessageDialog(QuestionDialogType) +} + +func WarningDialog() *MessageDialog { + return newMessageDialog(WarningDialogType) +} + +func ErrorDialog() *MessageDialog { + return newMessageDialog(ErrorDialogType) +} + +func OpenFileDialog() *OpenFileDialogStruct { + return newOpenFileDialog() +} + +func SaveFileDialog() *SaveFileDialogStruct { + return newSaveFileDialog() +} + +func (a *App) dispatchOnMainThread(fn func()) { + // If we are on the main thread, just call the function + if a.impl.isOnMainThread() { + fn() + return + } + + mainThreadFunctionStoreLock.Lock() + id := generateFunctionStoreID() + mainThreadFunctionStore[id] = fn + mainThreadFunctionStoreLock.Unlock() + // Call platform specific dispatch function + a.impl.dispatchOnMainThread(id) +} + +func (a *App) Hide() { + if a.impl != nil { + a.impl.hide() + } +} + +func (a *App) Show() { + if a.impl != nil { + a.impl.show() + } +} + +func (a *App) runOrDeferToAppRun(r runnable) { + a.runLock.Lock() + + if !a.running { + defer a.runLock.Unlock() // Defer unlocking for panic tolerance. + a.pendingRun = append(a.pendingRun, r) + return + } + + // Unlock immediately to prevent deadlocks. + // No TOC/TOU risk here because a.running can never switch back to false. + a.runLock.Unlock() + r.Run() +} + +func (a *App) handleWindowKeyEvent(event *windowKeyEvent) { + defer handlePanic() + // Get window from window map + a.windowsLock.RLock() + window, ok := a.windows[event.windowId] + a.windowsLock.RUnlock() + if !ok { + a.warning("WebviewWindow #%d not found", event.windowId) + return + } + // Get callback from window + window.HandleKeyEvent(event.acceleratorString) +} + +func (a *App) shouldQuit() bool { + if a.options.ShouldQuit != nil { + return a.options.ShouldQuit() + } + return true +} diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go new file mode 100644 index 000000000..31459ff55 --- /dev/null +++ b/v3/pkg/application/application_darwin.go @@ -0,0 +1,468 @@ +//go:build darwin + +package application + +/* + +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa -mmacosx-version-min=10.13 + +#include "application_darwin.h" +#include "application_darwin_delegate.h" +#include "webview_window_darwin.h" +#include + +extern void registerListener(unsigned int event); + +#import +#import + +static AppDelegate *appDelegate = nil; + +static void init(void) { + [NSApplication sharedApplication]; + appDelegate = [[AppDelegate alloc] init]; + [NSApp setDelegate:appDelegate]; + + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { + NSWindow* eventWindow = [event window]; + if (eventWindow == nil ) { + return event; + } + WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[eventWindow delegate]; + if (windowDelegate == nil) { + return event; + } + if ([windowDelegate respondsToSelector:@selector(handleLeftMouseDown:)]) { + [windowDelegate handleLeftMouseDown:event]; + } + return event; + }]; + + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { + NSWindow* eventWindow = [event window]; + if (eventWindow == nil ) { + return event; + } + WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[eventWindow delegate]; + if (windowDelegate == nil) { + return event; + } + if ([windowDelegate respondsToSelector:@selector(handleLeftMouseUp:)]) { + [windowDelegate handleLeftMouseUp:eventWindow]; + } + return event; + }]; + + NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter]; + [center addObserver:appDelegate selector:@selector(themeChanged:) name:@"AppleInterfaceThemeChangedNotification" object:nil]; + + // Register the custom URL scheme handler + StartCustomProtocolHandler(); +} + +static bool isDarkMode(void) { + NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; + if (userDefaults == nil) { + return false; + } + + NSString *interfaceStyle = [userDefaults stringForKey:@"AppleInterfaceStyle"]; + if (interfaceStyle == nil) { + return false; + } + + return [interfaceStyle isEqualToString:@"Dark"]; +} + +static char* getAccentColor(void) { + @autoreleasepool { + NSColor *accentColor; + if (@available(macOS 10.14, *)) { + accentColor = [NSColor controlAccentColor]; + } else { + // Fallback to system blue for older macOS versions + accentColor = [NSColor systemBlueColor]; + } + // Convert to RGB color space + NSColor *rgbColor = [accentColor colorUsingColorSpace:[NSColorSpace sRGBColorSpace]]; + if (rgbColor == nil) { + rgbColor = accentColor; + } + // Get RGB components + CGFloat red, green, blue, alpha; + [rgbColor getRed:&red green:&green blue:&blue alpha:&alpha]; + // Convert to 0-255 range and format as rgb() string + int r = (int)(red * 255); + int g = (int)(green * 255); + int b = (int)(blue * 255); + NSString *colorString = [NSString stringWithFormat:@"rgb(%d,%d,%d)", r, g, b]; + return strdup([colorString UTF8String]); + } +} + +static void setApplicationShouldTerminateAfterLastWindowClosed(bool shouldTerminate) { + // Get the NSApp delegate + AppDelegate *appDelegate = (AppDelegate*)[NSApp delegate]; + // Set the applicationShouldTerminateAfterLastWindowClosed boolean + appDelegate.shouldTerminateWhenLastWindowClosed = shouldTerminate; +} + +static void setActivationPolicy(int policy) { + [NSApp setActivationPolicy:policy]; +} + +static void activateIgnoringOtherApps() { + [NSApp activateIgnoringOtherApps:YES]; +} + +static void run(void) { + @autoreleasepool { + [NSApp run]; + [appDelegate release]; + [NSApp abortModal]; + } +} + +// destroyApp destroys the application +static void destroyApp(void) { + [NSApp terminate:nil]; +} + +// Set the application menu +static void setApplicationMenu(void *menu) { + NSMenu *nsMenu = (__bridge NSMenu *)menu; + [NSApp setMainMenu:menu]; +} + +// Get the application name +static char* getAppName(void) { + NSString *appName = [NSRunningApplication currentApplication].localizedName; + if( appName == nil ) { + appName = [[NSProcessInfo processInfo] processName]; + } + return strdup([appName UTF8String]); +} + +// get the current window ID +static unsigned int getCurrentWindowID(void) { + NSWindow *window = [NSApp keyWindow]; + // Get the window delegate + WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)[window delegate]; + return delegate.windowId; +} + +// Set the application icon +static void setApplicationIcon(void *icon, int length) { + // On main thread + dispatch_async(dispatch_get_main_queue(), ^{ + NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithBytes:icon length:length]]; + [NSApp setApplicationIconImage:image]; + }); +} + +// Hide the application +static void hide(void) { + [NSApp hide:nil]; +} + +// Show the application +static void show(void) { + [NSApp unhide:nil]; +} + +static const char* serializationNSDictionary(void *dict) { + @autoreleasepool { + NSDictionary *nsDict = (__bridge NSDictionary *)dict; + + if ([NSJSONSerialization isValidJSONObject:nsDict]) { + NSError *error; + NSData *data = [NSJSONSerialization dataWithJSONObject:nsDict options:kNilOptions error:&error]; + NSString *result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + + return strdup([result UTF8String]); + } + } + + return nil; +} + +static void startSingleInstanceListener(const char *uniqueID) { + // Convert to NSString + NSString *uid = [NSString stringWithUTF8String:uniqueID]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:appDelegate + selector:@selector(handleSecondInstanceNotification:) name:uid object:nil]; +} +*/ +import "C" +import ( + "encoding/json" + "unsafe" + + "github.com/wailsapp/wails/v3/internal/operatingsystem" + + "github.com/wailsapp/wails/v3/internal/assetserver/webview" + "github.com/wailsapp/wails/v3/pkg/events" +) + +type macosApp struct { + applicationMenu unsafe.Pointer + parent *App +} + +func (m *macosApp) isDarkMode() bool { + return bool(C.isDarkMode()) +} + +func (m *macosApp) getAccentColor() string { + accentColorC := C.getAccentColor() + defer C.free(unsafe.Pointer(accentColorC)) + return C.GoString(accentColorC) +} + +func getNativeApplication() *macosApp { + return globalApplication.impl.(*macosApp) +} + +func (m *macosApp) hide() { + C.hide() +} + +func (m *macosApp) show() { + C.show() +} + +func (m *macosApp) on(eventID uint) { + C.registerListener(C.uint(eventID)) +} + +func (m *macosApp) setIcon(icon []byte) { + C.setApplicationIcon(unsafe.Pointer(&icon[0]), C.int(len(icon))) +} + +func (m *macosApp) name() string { + appName := C.getAppName() + defer C.free(unsafe.Pointer(appName)) + return C.GoString(appName) +} + +func (m *macosApp) getCurrentWindowID() uint { + return uint(C.getCurrentWindowID()) +} + +func (m *macosApp) setApplicationMenu(menu *Menu) { + if menu == nil { + // Create a default menu for mac + menu = DefaultApplicationMenu() + } + menu.Update() + + // Convert impl to macosMenu object + m.applicationMenu = (menu.impl).(*macosMenu).nsMenu + C.setApplicationMenu(m.applicationMenu) +} + +func (m *macosApp) run() error { + if m.parent.options.SingleInstance != nil { + cUniqueID := C.CString(m.parent.options.SingleInstance.UniqueID) + defer C.free(unsafe.Pointer(cUniqueID)) + C.startSingleInstanceListener(cUniqueID) + } + // Add a hook to the ApplicationDidFinishLaunching event + m.parent.Event.OnApplicationEvent( + events.Mac.ApplicationDidFinishLaunching, + func(*ApplicationEvent) { + C.setApplicationShouldTerminateAfterLastWindowClosed( + C.bool(m.parent.options.Mac.ApplicationShouldTerminateAfterLastWindowClosed), + ) + C.setActivationPolicy(C.int(m.parent.options.Mac.ActivationPolicy)) + C.activateIgnoringOtherApps() + }, + ) + m.setupCommonEvents() + // setup event listeners + for eventID := range m.parent.applicationEventListeners { + m.on(eventID) + } + C.run() + return nil +} + +func (m *macosApp) destroy() { + C.destroyApp() +} + +func (m *macosApp) GetFlags(options Options) map[string]any { + if options.Flags == nil { + options.Flags = make(map[string]any) + } + return options.Flags +} + +func newPlatformApp(app *App) *macosApp { + C.init() + return &macosApp{ + parent: app, + } +} + +//export processApplicationEvent +func processApplicationEvent(eventID C.uint, data unsafe.Pointer) { + event := newApplicationEvent(events.ApplicationEventType(eventID)) + + if data != nil { + dataCStrJSON := C.serializationNSDictionary(data) + if dataCStrJSON != nil { + defer C.free(unsafe.Pointer(dataCStrJSON)) + + dataJSON := C.GoString(dataCStrJSON) + var result map[string]any + err := json.Unmarshal([]byte(dataJSON), &result) + + if err != nil { + panic(err) + } + + event.Context().setData(result) + } + } + + switch event.Id { + case uint(events.Mac.ApplicationDidChangeTheme): + isDark := globalApplication.Env.IsDarkMode() + event.Context().setIsDarkMode(isDark) + } + applicationEvents <- event +} + +//export processWindowEvent +func processWindowEvent(windowID C.uint, eventID C.uint) { + windowEvents <- &windowEvent{ + WindowID: uint(windowID), + EventID: uint(eventID), + } +} + +//export processMessage +func processMessage(windowID C.uint, message *C.char) { + windowMessageBuffer <- &windowMessage{ + windowId: uint(windowID), + message: C.GoString(message), + } +} + +//export processURLRequest +func processURLRequest(windowID C.uint, wkUrlSchemeTask unsafe.Pointer) { + window, ok := globalApplication.Window.GetByID(uint(windowID)) + if !ok || window == nil { + globalApplication.debug("could not find window with id: %d", windowID) + return + } + + webviewRequests <- &webViewAssetRequest{ + Request: webview.NewRequest(wkUrlSchemeTask), + windowId: uint(windowID), + windowName: window.Name(), + } +} + +//export processWindowKeyDownEvent +func processWindowKeyDownEvent(windowID C.uint, acceleratorString *C.char) { + windowKeyEvents <- &windowKeyEvent{ + windowId: uint(windowID), + acceleratorString: C.GoString(acceleratorString), + } +} + +//export processDragItems +func processDragItems(windowID C.uint, arr **C.char, length C.int, x C.int, y C.int) { + var filenames []string + // Convert the C array to a Go slice + goSlice := (*[1 << 30]*C.char)(unsafe.Pointer(arr))[:length:length] + for _, str := range goSlice { + filenames = append(filenames, C.GoString(str)) + } + + globalApplication.debug( + "[DragDropDebug] processDragItems called", + "windowID", + windowID, + "fileCount", + len(filenames), + "x", + x, + "y", + y, + ) + targetWindow, ok := globalApplication.Window.GetByID(uint(windowID)) + if !ok || targetWindow == nil { + println("Error: processDragItems could not find window with ID:", uint(windowID)) + return + } + + globalApplication.debug( + "[DragDropDebug] processDragItems: Calling targetWindow.InitiateFrontendDropProcessing", + ) + targetWindow.InitiateFrontendDropProcessing(filenames, int(x), int(y)) +} + +//export processMenuItemClick +func processMenuItemClick(menuID C.uint) { + menuItemClicked <- uint(menuID) +} + +//export shouldQuitApplication +func shouldQuitApplication() C.bool { + // TODO: This should be configurable + return C.bool(globalApplication.shouldQuit()) +} + +//export cleanup +func cleanup() { + globalApplication.cleanup() +} + +func (a *App) logPlatformInfo() { + info, err := operatingsystem.Info() + if err != nil { + a.error("error getting OS info: %w", err) + return + } + + a.info("Platform Info:", info.AsLogSlice()...) + +} + +func (a *App) platformEnvironment() map[string]any { + return map[string]any{} +} + +func fatalHandler(errFunc func(error)) { + return +} + +//export HandleOpenFile +func HandleOpenFile(filePath *C.char) { + goFilepath := C.GoString(filePath) + // Create new application event context + eventContext := newApplicationEventContext() + eventContext.setOpenedWithFile(goFilepath) + // EmitEvent application started event + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Common.ApplicationOpenedWithFile), + ctx: eventContext, + } +} + +//export HandleCustomProtocol +func HandleCustomProtocol(urlCString *C.char) { + urlString := C.GoString(urlCString) + eventContext := newApplicationEventContext() + eventContext.setURL(urlString) + + // Emit the standard event with the URL string as data + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Common.ApplicationLaunchedWithUrl), + ctx: eventContext, + } +} diff --git a/v3/pkg/application/application_darwin.h b/v3/pkg/application/application_darwin.h new file mode 100644 index 000000000..e67384397 --- /dev/null +++ b/v3/pkg/application/application_darwin.h @@ -0,0 +1,11 @@ +//go:build darwin + +#ifndef application_h +#define application_h + +static void init(void); +static void run(void); +static void setActivationPolicy(int policy); +static char *getAppName(void); + +#endif \ No newline at end of file diff --git a/v3/pkg/application/application_darwin_delegate.h b/v3/pkg/application/application_darwin_delegate.h new file mode 100644 index 000000000..77c30898b --- /dev/null +++ b/v3/pkg/application/application_darwin_delegate.h @@ -0,0 +1,25 @@ +//go:build darwin + +#ifndef appdelegate_h +#define appdelegate_h + +#import + +@interface AppDelegate : NSResponder +@property bool shouldTerminateWhenLastWindowClosed; +@property bool shuttingDown; +- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; +@end + +extern void HandleOpenFile(char *); + +// Declarations for Apple Event based custom URL handling +extern void HandleCustomProtocol(char*); + +@interface CustomProtocolSchemeHandler : NSObject ++ (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; +@end + +void StartCustomProtocolHandler(void); + +#endif /* appdelegate_h */ diff --git a/v3/pkg/application/application_darwin_delegate.m b/v3/pkg/application/application_darwin_delegate.m new file mode 100644 index 000000000..87504e62b --- /dev/null +++ b/v3/pkg/application/application_darwin_delegate.m @@ -0,0 +1,198 @@ +//go:build darwin +#import "application_darwin_delegate.h" +#import "../events/events_darwin.h" +#import // For Apple Event constants +extern bool hasListeners(unsigned int); +extern bool shouldQuitApplication(); +extern void cleanup(); +extern void handleSecondInstanceData(char * message); +@implementation AppDelegate +- (void)dealloc +{ + [super dealloc]; +} +-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename + { + const char* utf8FileName = filename.UTF8String; + HandleOpenFile((char*)utf8FileName); + return YES; + } +// Create the applicationShouldTerminateAfterLastWindowClosed: method +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication +{ + return self.shouldTerminateWhenLastWindowClosed; +} +- (void)themeChanged:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeTheme) ) { + processApplicationEvent(EventApplicationDidChangeTheme, NULL); + } +} +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + if( ! shouldQuitApplication() ) { + return NSTerminateCancel; + } + if( !self.shuttingDown ) { + self.shuttingDown = true; + cleanup(); + } + return NSTerminateNow; +} +- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app +{ + return YES; +} +- (BOOL)applicationShouldHandleReopen:(NSNotification *)notification + hasVisibleWindows:(BOOL)flag { // Changed from NSApplication to NSNotification + if( hasListeners(EventApplicationShouldHandleReopen) ) { + processApplicationEvent(EventApplicationShouldHandleReopen, @{@"hasVisibleWindows": @(flag)}); + } + + return TRUE; +} +- (void)handleSecondInstanceNotification:(NSNotification *)note; +{ + if (note.userInfo[@"message"] != nil) { + NSString *message = note.userInfo[@"message"]; + const char* utf8Message = message.UTF8String; + handleSecondInstanceData((char*)utf8Message); + } +} +// GENERATED EVENTS START +- (void)applicationDidBecomeActive:(NSNotification *)notification { + if( hasListeners(EventApplicationDidBecomeActive) ) { + processApplicationEvent(EventApplicationDidBecomeActive, NULL); + } +} + +- (void)applicationDidChangeBackingProperties:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeBackingProperties) ) { + processApplicationEvent(EventApplicationDidChangeBackingProperties, NULL); + } +} + +- (void)applicationDidChangeEffectiveAppearance:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeEffectiveAppearance) ) { + processApplicationEvent(EventApplicationDidChangeEffectiveAppearance, NULL); + } +} + +- (void)applicationDidChangeIcon:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeIcon) ) { + processApplicationEvent(EventApplicationDidChangeIcon, NULL); + } +} + +- (void)applicationDidChangeOcclusionState:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeOcclusionState) ) { + processApplicationEvent(EventApplicationDidChangeOcclusionState, NULL); + } +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeScreenParameters) ) { + processApplicationEvent(EventApplicationDidChangeScreenParameters, NULL); + } +} + +- (void)applicationDidChangeStatusBarFrame:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeStatusBarFrame) ) { + processApplicationEvent(EventApplicationDidChangeStatusBarFrame, NULL); + } +} + +- (void)applicationDidChangeStatusBarOrientation:(NSNotification *)notification { + if( hasListeners(EventApplicationDidChangeStatusBarOrientation) ) { + processApplicationEvent(EventApplicationDidChangeStatusBarOrientation, NULL); + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + if( hasListeners(EventApplicationDidFinishLaunching) ) { + processApplicationEvent(EventApplicationDidFinishLaunching, NULL); + } +} + +- (void)applicationDidHide:(NSNotification *)notification { + if( hasListeners(EventApplicationDidHide) ) { + processApplicationEvent(EventApplicationDidHide, NULL); + } +} + +- (void)applicationDidResignActive:(NSNotification *)notification { + if( hasListeners(EventApplicationDidResignActive) ) { + processApplicationEvent(EventApplicationDidResignActive, NULL); + } +} + +- (void)applicationDidUnhide:(NSNotification *)notification { + if( hasListeners(EventApplicationDidUnhide) ) { + processApplicationEvent(EventApplicationDidUnhide, NULL); + } +} + +- (void)applicationDidUpdate:(NSNotification *)notification { + if( hasListeners(EventApplicationDidUpdate) ) { + processApplicationEvent(EventApplicationDidUpdate, NULL); + } +} + +- (void)applicationWillBecomeActive:(NSNotification *)notification { + if( hasListeners(EventApplicationWillBecomeActive) ) { + processApplicationEvent(EventApplicationWillBecomeActive, NULL); + } +} + +- (void)applicationWillFinishLaunching:(NSNotification *)notification { + if( hasListeners(EventApplicationWillFinishLaunching) ) { + processApplicationEvent(EventApplicationWillFinishLaunching, NULL); + } +} + +- (void)applicationWillHide:(NSNotification *)notification { + if( hasListeners(EventApplicationWillHide) ) { + processApplicationEvent(EventApplicationWillHide, NULL); + } +} + +- (void)applicationWillResignActive:(NSNotification *)notification { + if( hasListeners(EventApplicationWillResignActive) ) { + processApplicationEvent(EventApplicationWillResignActive, NULL); + } +} + +- (void)applicationWillTerminate:(NSNotification *)notification { + if( hasListeners(EventApplicationWillTerminate) ) { + processApplicationEvent(EventApplicationWillTerminate, NULL); + } +} + +- (void)applicationWillUnhide:(NSNotification *)notification { + if( hasListeners(EventApplicationWillUnhide) ) { + processApplicationEvent(EventApplicationWillUnhide, NULL); + } +} + +- (void)applicationWillUpdate:(NSNotification *)notification { + if( hasListeners(EventApplicationWillUpdate) ) { + processApplicationEvent(EventApplicationWillUpdate, NULL); + } +} + +// GENERATED EVENTS END +@end +// Implementation for Apple Event based custom URL handling +@implementation CustomProtocolSchemeHandler ++ (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { + NSString *urlStr = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + if (urlStr) { + HandleCustomProtocol((char*)[urlStr UTF8String]); + } +} +@end +void StartCustomProtocolHandler(void) { + NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager]; + [appleEventManager setEventHandler:[CustomProtocolSchemeHandler class] + andSelector:@selector(handleGetURLEvent:withReplyEvent:) + forEventClass:kInternetEventClass + andEventID: kAEGetURL]; +} diff --git a/v3/pkg/application/application_debug.go b/v3/pkg/application/application_debug.go new file mode 100644 index 000000000..bc850c3e8 --- /dev/null +++ b/v3/pkg/application/application_debug.go @@ -0,0 +1,71 @@ +//go:build !production + +package application + +import ( + "github.com/go-git/go-git/v5" + "github.com/samber/lo" + "github.com/wailsapp/wails/v3/internal/version" + "path/filepath" + "runtime/debug" +) + +// BuildSettings contains the build settings for the application +var BuildSettings map[string]string + +// BuildInfo contains the build info for the application +var BuildInfo *debug.BuildInfo + +func init() { + var ok bool + BuildInfo, ok = debug.ReadBuildInfo() + if !ok { + return + } + BuildSettings = lo.Associate(BuildInfo.Settings, func(setting debug.BuildSetting) (string, string) { + return setting.Key, setting.Value + }) +} + +// We use this to patch the application to production mode. +func newApplication(options Options) *App { + result := &App{ + isDebugMode: true, + options: options, + } + result.init() + return result +} + +func (a *App) logStartup() { + var args []any + + // BuildInfo is nil when build with garble + if BuildInfo == nil { + return + } + + wailsPackage, _ := lo.Find(BuildInfo.Deps, func(dep *debug.Module) bool { + return dep.Path == "github.com/wailsapp/wails/v3" + }) + + wailsVersion := version.String() + if wailsPackage != nil && wailsPackage.Replace != nil { + wailsVersion = "(local) => " + filepath.ToSlash(wailsPackage.Replace.Path) + // Get the latest commit hash + repo, err := git.PlainOpen(filepath.Join(wailsPackage.Replace.Path, "..")) + if err == nil { + head, err := repo.Head() + if err == nil { + wailsVersion += " (" + head.Hash().String()[:8] + ")" + } + } + } + args = append(args, "Wails", wailsVersion) + args = append(args, "Compiler", BuildInfo.GoVersion) + for key, value := range BuildSettings { + args = append(args, key, value) + } + + a.info("Build Info:", args...) +} diff --git a/v3/pkg/application/application_dev.go b/v3/pkg/application/application_dev.go new file mode 100644 index 000000000..e12033e33 --- /dev/null +++ b/v3/pkg/application/application_dev.go @@ -0,0 +1,51 @@ +//go:build !production + +package application + +import ( + "net/http" + "time" + + "github.com/wailsapp/wails/v3/internal/assetserver" +) + +var devMode = false + +func (a *App) preRun() error { + // Check for frontend server url + frontendURL := assetserver.GetDevServerURL() + if frontendURL != "" { + devMode = true + // We want to check if the frontend server is running by trying to http get the url + // and if it is not, we wait 500ms and try again for a maximum of 10 times. If it is + // still not available, we return an error. + // This is to allow the frontend server to start up before the backend server. + client := http.Client{} + a.Logger.Info("Waiting for frontend dev server to start...", "url", frontendURL) + for i := 0; i < 10; i++ { + _, err := client.Get(frontendURL) + if err == nil { + a.Logger.Info("Connected to frontend dev server!") + return nil + } + // Wait 500ms + time.Sleep(500 * time.Millisecond) + if i%2 == 0 { + a.Logger.Info("Retrying...") + } + } + a.fatal("unable to connect to frontend server. Please check it is running - FRONTEND_DEVSERVER_URL='%s'", frontendURL) + } + return nil +} + +func (a *App) postQuit() { + if devMode { + a.Logger.Info("The application has terminated, but the watcher is still running.") + a.Logger.Info("To terminate the watcher, press CTRL+C") + } +} + +func (a *App) enableDevTools() { + +} diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go new file mode 100644 index 000000000..b92c2c6aa --- /dev/null +++ b/v3/pkg/application/application_linux.go @@ -0,0 +1,309 @@ +//go:build linux + +package application + +/* + #include "gtk/gtk.h" + #include "webkit2/webkit2.h" + static guint get_compiled_gtk_major_version() { return GTK_MAJOR_VERSION; } + static guint get_compiled_gtk_minor_version() { return GTK_MINOR_VERSION; } + static guint get_compiled_gtk_micro_version() { return GTK_MICRO_VERSION; } + static guint get_compiled_webkit_major_version() { return WEBKIT_MAJOR_VERSION; } + static guint get_compiled_webkit_minor_version() { return WEBKIT_MINOR_VERSION; } + static guint get_compiled_webkit_micro_version() { return WEBKIT_MICRO_VERSION; } +*/ +import "C" +import ( + "fmt" + "os" + "slices" + "strings" + "sync" + + "path/filepath" + + "github.com/godbus/dbus/v5" + "github.com/wailsapp/wails/v3/internal/operatingsystem" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func init() { + // FIXME: This should be handled appropriately in the individual files most likely. + // Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings + if os.Getenv("GDK_BACKEND") == "" && + (os.Getenv("XDG_SESSION_TYPE") == "" || os.Getenv("XDG_SESSION_TYPE") == "unspecified" || os.Getenv("XDG_SESSION_TYPE") == "x11") { + _ = os.Setenv("GDK_BACKEND", "x11") + } +} + +type linuxApp struct { + application pointer + parent *App + + startupActions []func() + + // Native -> uint + windowMap map[windowPointer]uint + windowMapLock sync.Mutex + + theme string + + icon pointer +} + +func (a *linuxApp) GetFlags(options Options) map[string]any { + if options.Flags == nil { + options.Flags = make(map[string]any) + } + return options.Flags +} + +func getNativeApplication() *linuxApp { + return globalApplication.impl.(*linuxApp) +} + +func (a *linuxApp) hide() { + a.hideAllWindows() +} + +func (a *linuxApp) show() { + a.showAllWindows() +} + +func (a *linuxApp) on(eventID uint) { + // TODO: Test register/unregister events + //C.registerApplicationEvent(l.application, C.uint(eventID)) +} + +func (a *linuxApp) name() string { + return appName() +} + +type rnr struct { + f func() +} + +func (r rnr) run() { + r.f() +} + +func (a *linuxApp) setApplicationMenu(menu *Menu) { + // FIXME: How do we avoid putting a menu? + if menu == nil { + // Create a default menu + menu = DefaultApplicationMenu() + globalApplication.applicationMenu = menu + } +} + +func (a *linuxApp) run() error { + + if len(os.Args) == 2 { // Case: program + 1 argument + arg1 := os.Args[1] + // Check if the argument is likely a URL from a custom protocol invocation + if strings.Contains(arg1, "://") { + a.parent.info("Application launched with argument, potentially a URL from custom protocol", "url", arg1) + eventContext := newApplicationEventContext() + eventContext.setURL(arg1) + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Common.ApplicationLaunchedWithUrl), + ctx: eventContext, + } + } else { + // Check if the argument matches any file associations + if a.parent.options.FileAssociations != nil { + ext := filepath.Ext(arg1) + if slices.Contains(a.parent.options.FileAssociations, ext) { + a.parent.info("File opened via file association", "file", arg1, "extension", ext) + eventContext := newApplicationEventContext() + eventContext.setOpenedWithFile(arg1) + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Common.ApplicationOpenedWithFile), + ctx: eventContext, + } + return nil + } + } + a.parent.info("Application launched with single argument (not a URL), potential file open?", "arg", arg1) + } + } else if len(os.Args) > 2 { + // Log if multiple arguments are passed + a.parent.info("Application launched with multiple arguments", "args", os.Args[1:]) + } + + a.parent.Event.OnApplicationEvent(events.Linux.ApplicationStartup, func(evt *ApplicationEvent) { + // TODO: What should happen here? + }) + a.setupCommonEvents() + a.monitorThemeChanges() + return appRun(a.application) +} + +func (a *linuxApp) unregisterWindow(w windowPointer) { + a.windowMapLock.Lock() + delete(a.windowMap, w) + a.windowMapLock.Unlock() + + // If this was the last window... + if len(a.windowMap) == 0 && !a.parent.options.Linux.DisableQuitOnLastWindowClosed { + a.destroy() + } +} + +func (a *linuxApp) destroy() { + if !globalApplication.shouldQuit() { + return + } + globalApplication.cleanup() + appDestroy(a.application) +} + +func (a *linuxApp) isOnMainThread() bool { + return isOnMainThread() +} + +// register our window to our parent mapping +func (a *linuxApp) registerWindow(window pointer, id uint) { + a.windowMapLock.Lock() + a.windowMap[windowPointer(window)] = id + a.windowMapLock.Unlock() +} + +func (a *linuxApp) isDarkMode() bool { + return strings.Contains(a.theme, "dark") +} + +func (a *linuxApp) getAccentColor() string { + // Linux doesn't have a unified system accent color API + // Return a default blue color + return "rgb(0,122,255)" +} + +func (a *linuxApp) monitorThemeChanges() { + go func() { + defer handlePanic() + conn, err := dbus.ConnectSessionBus() + if err != nil { + a.parent.info( + "[WARNING] Failed to connect to session bus; monitoring for theme changes will not function:", + err, + ) + return + } + defer conn.Close() + + if err = conn.AddMatchSignal( + dbus.WithMatchObjectPath("/org/freedesktop/portal/desktop"), + ); err != nil { + panic(err) + } + + c := make(chan *dbus.Signal, 10) + conn.Signal(c) + + getTheme := func(body []interface{}) (string, bool) { + if len(body) < 2 { + return "", false + } + if entry, ok := body[0].(string); !ok || entry != "org.gnome.desktop.interface" { + return "", false + } + if entry, ok := body[1].(string); ok && entry == "color-scheme" { + return body[2].(dbus.Variant).Value().(string), true + } + return "", false + } + + for v := range c { + theme, ok := getTheme(v.Body) + if !ok { + continue + } + + if theme != a.theme { + a.theme = theme + event := newApplicationEvent(events.Linux.SystemThemeChanged) + event.Context().setIsDarkMode(a.isDarkMode()) + applicationEvents <- event + } + + } + }() +} + +func newPlatformApp(parent *App) *linuxApp { + + name := strings.ToLower(strings.Replace(parent.options.Name, " ", "", -1)) + if name == "" { + name = "undefined" + } + app := &linuxApp{ + parent: parent, + application: appNew(name), + windowMap: map[windowPointer]uint{}, + } + + if parent.options.Linux.ProgramName != "" { + setProgramName(parent.options.Linux.ProgramName) + } + + return app +} + +// logPlatformInfo logs the platform information to the console +func (a *App) logPlatformInfo() { + info, err := operatingsystem.Info() + if err != nil { + a.error("error getting OS info: %w", err) + return + } + + wkVersion := operatingsystem.GetWebkitVersion() + platformInfo := info.AsLogSlice() + platformInfo = append(platformInfo, "Webkit2Gtk", wkVersion) + + a.info("Platform Info:", platformInfo...) +} + +//export processWindowEvent +func processWindowEvent(windowID C.uint, eventID C.uint) { + windowEvents <- &windowEvent{ + WindowID: uint(windowID), + EventID: uint(eventID), + } +} + +func buildVersionString(major, minor, micro C.uint) string { + return fmt.Sprintf("%d.%d.%d", uint(major), uint(minor), uint(micro)) +} + +func (a *App) platformEnvironment() map[string]any { + result := map[string]any{} + result["gtk3-compiled"] = buildVersionString( + C.get_compiled_gtk_major_version(), + C.get_compiled_gtk_minor_version(), + C.get_compiled_gtk_micro_version(), + ) + result["gtk3-runtime"] = buildVersionString( + C.gtk_get_major_version(), + C.gtk_get_minor_version(), + C.gtk_get_micro_version(), + ) + + result["webkit2gtk-compiled"] = buildVersionString( + C.get_compiled_webkit_major_version(), + C.get_compiled_webkit_minor_version(), + C.get_compiled_webkit_micro_version(), + ) + result["webkit2gtk-runtime"] = buildVersionString( + C.webkit_get_major_version(), + C.webkit_get_minor_version(), + C.webkit_get_micro_version(), + ) + return result +} + +func fatalHandler(errFunc func(error)) { + // Stub for windows function + return +} diff --git a/v3/pkg/application/application_options.go b/v3/pkg/application/application_options.go new file mode 100644 index 000000000..a9f8cf2f4 --- /dev/null +++ b/v3/pkg/application/application_options.go @@ -0,0 +1,225 @@ +package application + +import ( + "io/fs" + "log/slog" + "net/http" + + "github.com/wailsapp/wails/v3/internal/assetserver" +) + +// Options contains the options for the application +type Options struct { + // Name is the name of the application (used in the default about box) + Name string + + // Description is the description of the application (used in the default about box) + Description string + + // Icon is the icon of the application (used in the default about box) + Icon []byte + + // Mac is the Mac specific configuration for Mac builds + Mac MacOptions + + // Windows is the Windows specific configuration for Windows builds + Windows WindowsOptions + + // Linux is the Linux specific configuration for Linux builds + Linux LinuxOptions + + // Services allows you to bind Go methods to the frontend. + Services []Service + + // MarshalError will be called if non-nil + // to marshal to JSON the error values returned by service methods. + // + // MarshalError is not allowed to fail, + // but it may return a nil slice to fall back + // to the default error handling mechanism. + // + // If the returned slice is not nil, it must contain valid JSON. + MarshalError func(error) []byte + + // BindAliases allows you to specify alias IDs for your bound methods. + // Example: `BindAliases: map[uint32]uint32{1: 1411160069}` states that alias ID 1 maps to the Go method with ID 1411160069. + BindAliases map[uint32]uint32 + + // Logger is a slog.Logger instance used for logging Wails system messages (not application messages). + // If not defined, a default logger is used. + Logger *slog.Logger + + // LogLevel defines the log level of the Wails system logger. + LogLevel slog.Level + + // Assets are the application assets to be used. + Assets AssetOptions + + // Flags are key value pairs that are available to the frontend. + // This is also used by Wails to provide information to the frontend. + Flags map[string]any + + // PanicHandler is called when a panic occurs + PanicHandler func(*PanicDetails) + + // DisableDefaultSignalHandler disables the default signal handler + DisableDefaultSignalHandler bool + + // KeyBindings is a map of key bindings to functions + KeyBindings map[string]func(window Window) + + // OnShutdown is called when the application is about to terminate. + // This is useful for cleanup tasks. + // The shutdown process blocks until this function returns. + OnShutdown func() + + // PostShutdown is called after the application + // has finished shutting down, just before process termination. + // This is useful for testing and logging purposes + // on platforms where the Run() method does not return. + // When PostShutdown is called, the application instance is not usable anymore. + // The shutdown process blocks until this function returns. + PostShutdown func() + + // ShouldQuit is a function that is called when the user tries to quit the application. + // If the function returns true, the application will quit. + // If the function returns false, the application will not quit. + ShouldQuit func() bool + + // RawMessageHandler is called when the frontend sends a raw message. + // This is useful for implementing custom frontend-to-backend communication. + RawMessageHandler func(window Window, message string) + + // WarningHandler is called when a warning occurs + WarningHandler func(string) + + // ErrorHandler is called when an error occurs + ErrorHandler func(err error) + + // File extensions associated with the application + // Example: [".txt", ".md"] + // The '.' is required + FileAssociations []string + + // SingleInstance options for single instance functionality + SingleInstance *SingleInstanceOptions +} + +// AssetOptions defines the configuration of the AssetServer. +type AssetOptions struct { + // Handler which serves all the content to the WebView. + Handler http.Handler + + // Middleware is a HTTP Middleware which allows to hook into the AssetServer request chain. It allows to skip the default + // request handler dynamically, e.g. implement specialized Routing etc. + // The Middleware is called to build a new `http.Handler` used by the AssetSever and it also receives the default + // handler used by the AssetServer as an argument. + // + // This middleware injects itself before any of Wails internal middlewares. + // + // If not defined, the default AssetServer request chain is executed. + // + // Multiple Middlewares can be chained together with: + // ChainMiddleware(middleware ...Middleware) Middleware + Middleware Middleware + + // DisableLogging disables logging of the AssetServer. By default, the AssetServer logs every request. + DisableLogging bool +} + +// Middleware defines HTTP middleware that can be applied to the AssetServer. +// The handler passed as next is the next handler in the chain. One can decide to call the next handler +// or implement a specialized handling. +type Middleware func(next http.Handler) http.Handler + +// ChainMiddleware allows chaining multiple middlewares to one middleware. +func ChainMiddleware(middleware ...Middleware) Middleware { + return func(h http.Handler) http.Handler { + for i := len(middleware) - 1; i >= 0; i-- { + h = middleware[i](h) + } + return h + } +} + +// AssetFileServerFS returns a http handler which serves the assets from the fs.FS. +// If an external devserver has been provided 'FRONTEND_DEVSERVER_URL' the files are being served +// from the external server, ignoring the `assets`. +func AssetFileServerFS(assets fs.FS) http.Handler { + return assetserver.NewAssetFileServer(assets) +} + +// BundledAssetFileServer returns a http handler which serves the assets from the fs.FS. +// If an external devserver has been provided 'FRONTEND_DEVSERVER_URL' the files are being served +// from the external server, ignoring the `assets`. +// It also serves the compiled runtime.js file at `/wails/runtime.js`. +// It will provide the production runtime.js file from the embedded assets if the `production` tag is used. +func BundledAssetFileServer(assets fs.FS) http.Handler { + return assetserver.NewBundledAssetFileServer(assets) +} + +/******** Mac Options ********/ + +// ActivationPolicy is the activation policy for the application. +type ActivationPolicy int + +const ( + // ActivationPolicyRegular is used for applications that have a user interface, + ActivationPolicyRegular ActivationPolicy = iota + // ActivationPolicyAccessory is used for applications that do not have a main window, + // such as system tray applications or background applications. + ActivationPolicyAccessory + ActivationPolicyProhibited +) + +// MacOptions contains options for macOS applications. +type MacOptions struct { + // ActivationPolicy is the activation policy for the application. Defaults to + // applicationActivationPolicyRegular. + ActivationPolicy ActivationPolicy + // If set to true, the application will terminate when the last window is closed. + ApplicationShouldTerminateAfterLastWindowClosed bool +} + +/****** Windows Options *******/ + +// WindowsOptions contains options for Windows applications. +type WindowsOptions struct { + + // Window class name + // Default: WailsWebviewWindow + WndClass string + + // WndProcInterceptor is a function that will be called for every message sent in the application. + // Use this to hook into the main message loop. This is useful for handling custom window messages. + // If `shouldReturn` is `true` then `returnCode` will be returned by the main message loop. + // If `shouldReturn` is `false` then returnCode will be ignored and the message will be processed by the main message loop. + WndProcInterceptor func(hwnd uintptr, msg uint32, wParam, lParam uintptr) (returnCode uintptr, shouldReturn bool) + + // DisableQuitOnLastWindowClosed disables the auto quit of the application if the last window has been closed. + DisableQuitOnLastWindowClosed bool + + // Path where the WebView2 stores the user data. If empty %APPDATA%\[BinaryName.exe] will be used. + // If the path is not valid, a messagebox will be displayed with the error and the app will exit with error code. + WebviewUserDataPath string + + // Path to the directory with WebView2 executables. If empty WebView2 installed in the system will be used. + WebviewBrowserPath string +} + +/********* Linux Options *********/ + +// LinuxOptions contains options for Linux applications. +type LinuxOptions struct { + // DisableQuitOnLastWindowClosed disables the auto quit of the application if the last window has been closed. + DisableQuitOnLastWindowClosed bool + + // ProgramName is used to set the program's name for the window manager via GTK's g_set_prgname(). + //This name should not be localized. [see the docs] + // + //When a .desktop file is created this value helps with window grouping and desktop icons when the .desktop file's Name + //property differs form the executable's filename. + // + //[see the docs]: https://docs.gtk.org/glib/func.set_prgname.html + ProgramName string +} diff --git a/v3/pkg/application/application_production.go b/v3/pkg/application/application_production.go new file mode 100644 index 000000000..75f86b44d --- /dev/null +++ b/v3/pkg/application/application_production.go @@ -0,0 +1,18 @@ +//go:build production + +package application + +func newApplication(options Options) *App { + result := &App{ + isDebugMode: false, + options: options, + } + result.init() + return result +} + +func (a *App) logStartup() {} + +func (a *App) preRun() error { return nil } + +func (a *App) postQuit() error { return nil } diff --git a/v3/pkg/application/application_windows.go b/v3/pkg/application/application_windows.go new file mode 100644 index 000000000..f72abf963 --- /dev/null +++ b/v3/pkg/application/application_windows.go @@ -0,0 +1,434 @@ +//go:build windows + +package application + +import ( + "errors" + "os" + "path/filepath" + "slices" + "strings" + "sync" + "sync/atomic" + "syscall" + "time" + "unsafe" + + "github.com/wailsapp/go-webview2/webviewloader" + "github.com/wailsapp/wails/v3/internal/operatingsystem" + + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/w32" +) + +var ( + wmTaskbarCreated = w32.RegisterWindowMessage(w32.MustStringToUTF16Ptr("TaskbarCreated")) +) + +type windowsApp struct { + parent *App + + windowClass w32.WNDCLASSEX + instance w32.HINSTANCE + + windowMap map[w32.HWND]*windowsWebviewWindow + windowMapLock sync.RWMutex + + systrayMap map[w32.HMENU]*windowsSystemTray + systrayMapLock sync.RWMutex + + mainThreadID w32.HANDLE + mainThreadWindowHWND w32.HWND + + // Windows hidden by application.Hide() + hiddenWindows []*windowsWebviewWindow + focusedWindow w32.HWND + + // system theme + isCurrentlyDarkMode bool + currentWindowID uint + + // Restart taskbar flag + restartingTaskbar atomic.Bool +} + +func (m *windowsApp) isDarkMode() bool { + return w32.IsCurrentlyDarkMode() +} + +func (m *windowsApp) getAccentColor() string { + accentColor, err := w32.GetAccentColor() + if err != nil { + m.parent.error("failed to get accent color: %w", err) + return "rgb(0,122,255)" + } + + return accentColor +} + +func (m *windowsApp) isOnMainThread() bool { + return m.mainThreadID == w32.GetCurrentThreadId() +} + +func (m *windowsApp) GetFlags(options Options) map[string]any { + if options.Flags == nil { + options.Flags = make(map[string]any) + } + options.Flags["system"] = map[string]any{ + "resizeHandleWidth": w32.GetSystemMetrics(w32.SM_CXSIZEFRAME), + "resizeHandleHeight": w32.GetSystemMetrics(w32.SM_CYSIZEFRAME), + } + return options.Flags +} + +func (m *windowsApp) getWindowForHWND(hwnd w32.HWND) *windowsWebviewWindow { + m.windowMapLock.RLock() + defer m.windowMapLock.RUnlock() + return m.windowMap[hwnd] +} + +func getNativeApplication() *windowsApp { + return globalApplication.impl.(*windowsApp) +} + +func (m *windowsApp) hide() { + // Get the current focussed window + m.focusedWindow = w32.GetForegroundWindow() + + // Iterate over all windows and hide them if they aren't already hidden + for _, window := range m.windowMap { + if window.isVisible() { + // Add to hidden windows + m.hiddenWindows = append(m.hiddenWindows, window) + window.hide() + } + } + // Switch focus to the next application + hwndNext := w32.GetWindow(m.mainThreadWindowHWND, w32.GW_HWNDNEXT) + w32.SetForegroundWindow(hwndNext) +} + +func (m *windowsApp) show() { + // Iterate over all windows and show them if they were previously hidden + for _, window := range m.hiddenWindows { + window.show() + } + // Show the foreground window + w32.SetForegroundWindow(m.focusedWindow) +} + +func (m *windowsApp) on(_ uint) { +} + +func (m *windowsApp) setIcon(_ []byte) { +} + +func (m *windowsApp) name() string { + //appName := C.getAppName() + //defer C.free(unsafe.Pointer(appName)) + //return C.GoString(appName) + return "" +} + +func (m *windowsApp) getCurrentWindowID() uint { + return m.currentWindowID +} + +func (m *windowsApp) setApplicationMenu(menu *Menu) { + if menu == nil { + // Create a default menu for windows + menu = DefaultApplicationMenu() + } + menu.Update() + + m.parent.applicationMenu = menu +} + +func (m *windowsApp) run() error { + m.setupCommonEvents() + for eventID := range m.parent.applicationEventListeners { + m.on(eventID) + } + // EmitEvent application started event + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Windows.ApplicationStarted), + ctx: blankApplicationEventContext, + } + + if len(os.Args) == 2 { // Case: program + 1 argument + arg1 := os.Args[1] + // Check if the argument is likely a URL from a custom protocol invocation + if strings.Contains(arg1, "://") { + m.parent.info("Application launched with argument, potentially a URL from custom protocol", "url", arg1) + eventContext := newApplicationEventContext() + eventContext.setURL(arg1) + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Common.ApplicationLaunchedWithUrl), + ctx: eventContext, + } + } else { + // If not a URL-like string, check for file association + if m.parent.options.FileAssociations != nil { + ext := filepath.Ext(arg1) + if slices.Contains(m.parent.options.FileAssociations, ext) { + m.parent.info("Application launched with file via file association", "file", arg1) + eventContext := newApplicationEventContext() + eventContext.setOpenedWithFile(arg1) + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Common.ApplicationOpenedWithFile), + ctx: eventContext, + } + } + } + } + } else if len(os.Args) > 2 { + // Log if multiple arguments are passed, though typical protocol/file launch is a single arg. + m.parent.info("Application launched with multiple arguments", "args", os.Args[1:]) + } + + _ = m.runMainLoop() + + return nil +} + +func (m *windowsApp) destroy() { + if !globalApplication.shouldQuit() { + return + } + globalApplication.cleanup() + // destroy the main thread window + w32.DestroyWindow(m.mainThreadWindowHWND) + // Post a quit message to the main thread + w32.PostQuitMessage(0) +} + +func (m *windowsApp) init() { + // Register the window class + + icon := w32.LoadIconWithResourceID(m.instance, w32.IDI_APPLICATION) + + m.windowClass.Size = uint32(unsafe.Sizeof(m.windowClass)) + m.windowClass.Style = w32.CS_HREDRAW | w32.CS_VREDRAW + m.windowClass.WndProc = syscall.NewCallback(m.wndProc) + m.windowClass.Instance = m.instance + m.windowClass.Background = w32.COLOR_BTNFACE + 1 + m.windowClass.Icon = icon + m.windowClass.Cursor = w32.LoadCursorWithResourceID(0, w32.IDC_ARROW) + m.windowClass.ClassName = w32.MustStringToUTF16Ptr(m.parent.options.Windows.WndClass) + m.windowClass.MenuName = nil + m.windowClass.IconSm = icon + + if ret := w32.RegisterClassEx(&m.windowClass); ret == 0 { + panic(syscall.GetLastError()) + } + m.isCurrentlyDarkMode = w32.IsCurrentlyDarkMode() +} + +func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr) uintptr { + + // Handle the invoke callback + if msg == wmInvokeCallback { + m.invokeCallback(wParam, lParam) + return 0 + } + + // If the WndProcInterceptor is set in options, pass the message on + if m.parent.options.Windows.WndProcInterceptor != nil { + returnValue, shouldReturn := m.parent.options.Windows.WndProcInterceptor(hwnd, msg, wParam, lParam) + if shouldReturn { + return returnValue + } + } + + // Handle the main thread window + // Quit the application if requested + // Reprocess and cache screens when display settings change + if hwnd == m.mainThreadWindowHWND { + if msg == w32.WM_ENDSESSION || msg == w32.WM_DESTROY || msg == w32.WM_CLOSE { + globalApplication.Quit() + } + if msg == w32.WM_DISPLAYCHANGE || (msg == w32.WM_SETTINGCHANGE && wParam == w32.SPI_SETWORKAREA) { + err := m.processAndCacheScreens() + if err != nil { + m.parent.handleError(err) + } + } + } + + switch msg { + case wmTaskbarCreated: + if m.restartingTaskbar.Load() { + break + } + m.restartingTaskbar.Store(true) + m.reshowSystrays() + go func() { + // 1 second debounce + time.Sleep(1000) + m.restartingTaskbar.Store(false) + }() + case w32.WM_SETTINGCHANGE: + settingChanged := w32.UTF16PtrToString((*uint16)(unsafe.Pointer(lParam))) + if settingChanged == "ImmersiveColorSet" { + isDarkMode := w32.IsCurrentlyDarkMode() + if isDarkMode != m.isCurrentlyDarkMode { + eventContext := newApplicationEventContext() + eventContext.setIsDarkMode(isDarkMode) + applicationEvents <- &ApplicationEvent{ + Id: uint(events.Windows.SystemThemeChanged), + ctx: eventContext, + } + m.isCurrentlyDarkMode = isDarkMode + } + } + return 0 + case w32.WM_POWERBROADCAST: + switch wParam { + case w32.PBT_APMPOWERSTATUSCHANGE: + applicationEvents <- newApplicationEvent(events.Windows.APMPowerStatusChange) + case w32.PBT_APMSUSPEND: + applicationEvents <- newApplicationEvent(events.Windows.APMSuspend) + case w32.PBT_APMRESUMEAUTOMATIC: + applicationEvents <- newApplicationEvent(events.Windows.APMResumeAutomatic) + case w32.PBT_APMRESUMESUSPEND: + applicationEvents <- newApplicationEvent(events.Windows.APMResumeSuspend) + case w32.PBT_POWERSETTINGCHANGE: + applicationEvents <- newApplicationEvent(events.Windows.APMPowerSettingChange) + } + return 0 + } + + if window, ok := m.windowMap[hwnd]; ok { + return window.WndProc(msg, wParam, lParam) + } + + m.systrayMapLock.Lock() + systray, ok := m.systrayMap[hwnd] + m.systrayMapLock.Unlock() + if ok { + return systray.wndProc(msg, wParam, lParam) + } + + // Dispatch the message to the appropriate window + + return w32.DefWindowProc(hwnd, msg, wParam, lParam) +} + +func (m *windowsApp) registerWindow(result *windowsWebviewWindow) { + m.windowMapLock.Lock() + m.windowMap[result.hwnd] = result + m.windowMapLock.Unlock() +} + +func (m *windowsApp) registerSystemTray(result *windowsSystemTray) { + m.systrayMapLock.Lock() + defer m.systrayMapLock.Unlock() + m.systrayMap[result.hwnd] = result +} + +func (m *windowsApp) unregisterSystemTray(result *windowsSystemTray) { + m.systrayMapLock.Lock() + defer m.systrayMapLock.Unlock() + delete(m.systrayMap, result.hwnd) +} + +func (m *windowsApp) unregisterWindow(w *windowsWebviewWindow) { + m.windowMapLock.Lock() + delete(m.windowMap, w.hwnd) + m.windowMapLock.Unlock() + + // If this was the last window... + if len(m.windowMap) == 0 && !m.parent.options.Windows.DisableQuitOnLastWindowClosed { + w32.PostQuitMessage(0) + } +} + +func (m *windowsApp) reshowSystrays() { + m.systrayMapLock.Lock() + defer m.systrayMapLock.Unlock() + for _, systray := range m.systrayMap { + systray.reshow() + } +} + +func setupDPIAwareness() error { + // https://learn.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process + // https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows + + if w32.HasSetProcessDpiAwarenessContextFunc() { + // This is most recent version with the best results + // supported beginning with Windows 10, version 1703 + return w32.SetProcessDpiAwarenessContext(w32.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) + } + + if w32.HasSetProcessDpiAwarenessFunc() { + // Supported beginning with Windows 8.1 + return w32.SetProcessDpiAwareness(w32.PROCESS_PER_MONITOR_DPI_AWARE) + } + + if w32.HasSetProcessDPIAwareFunc() { + // If none of the above is supported, fallback to SetProcessDPIAware + // which is supported beginning with Windows Vista + return w32.SetProcessDPIAware() + } + + return errors.New("no DPI awareness method supported") +} + +func newPlatformApp(app *App) *windowsApp { + + err := setupDPIAwareness() + if err != nil { + app.handleError(err) + } + + result := &windowsApp{ + parent: app, + instance: w32.GetModuleHandle(""), + windowMap: make(map[w32.HWND]*windowsWebviewWindow), + systrayMap: make(map[w32.HWND]*windowsSystemTray), + } + + err = result.processAndCacheScreens() + if err != nil { + app.handleFatalError(err) + } + + result.init() + result.initMainLoop() + + return result +} + +func (a *App) logPlatformInfo() { + var args []any + args = append(args, "Go-WebView2Loader", webviewloader.UsingGoWebview2Loader) + webviewVersion, err := webviewloader.GetAvailableCoreWebView2BrowserVersionString( + a.options.Windows.WebviewBrowserPath, + ) + if err != nil { + args = append(args, "WebView2", "Error: "+err.Error()) + } else { + args = append(args, "WebView2", webviewVersion) + } + + osInfo, _ := operatingsystem.Info() + args = append(args, osInfo.AsLogSlice()...) + + a.info("Platform Info:", args...) +} + +func (a *App) platformEnvironment() map[string]any { + result := map[string]any{} + webviewVersion, _ := webviewloader.GetAvailableCoreWebView2BrowserVersionString( + a.options.Windows.WebviewBrowserPath, + ) + result["Go-WebView2Loader"] = webviewloader.UsingGoWebview2Loader + result["WebView2"] = webviewVersion + return result +} + +func fatalHandler(errFunc func(error)) { + w32.Fatal = errFunc + return +} diff --git a/v3/pkg/application/assets/alpha/index.html b/v3/pkg/application/assets/alpha/index.html new file mode 100644 index 000000000..3c685de17 --- /dev/null +++ b/v3/pkg/application/assets/alpha/index.html @@ -0,0 +1,81 @@ + + + + + Wails Alpha + + + + + + +
Alpha
+
+

Documentation

+

Feedback

+
+ + diff --git a/v3/pkg/application/bindings.go b/v3/pkg/application/bindings.go new file mode 100644 index 000000000..431caab62 --- /dev/null +++ b/v3/pkg/application/bindings.go @@ -0,0 +1,398 @@ +package application + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "reflect" + "runtime" + "strings" + + "github.com/wailsapp/wails/v3/internal/hash" + + "github.com/samber/lo" +) + +type CallOptions struct { + MethodID uint32 `json:"methodID"` + MethodName string `json:"methodName"` + Args []json.RawMessage `json:"args"` +} + +type ErrorKind string + +const ( + ReferenceError ErrorKind = "ReferenceError" + TypeError ErrorKind = "TypeError" + RuntimeError ErrorKind = "RuntimeError" +) + +type CallError struct { + Kind ErrorKind `json:"kind"` + Message string `json:"message"` + Cause any `json:"cause,omitempty"` +} + +func (e *CallError) Error() string { + return e.Message +} + +// Parameter defines a Go method parameter +type Parameter struct { + Name string `json:"name,omitempty"` + TypeName string `json:"type"` + ReflectType reflect.Type +} + +func newParameter(Name string, Type reflect.Type) *Parameter { + return &Parameter{ + Name: Name, + TypeName: Type.String(), + ReflectType: Type, + } +} + +// IsType returns true if the given +func (p *Parameter) IsType(typename string) bool { + return p.TypeName == typename +} + +// IsError returns true if the parameter type is an error +func (p *Parameter) IsError() bool { + return p.IsType("error") +} + +// BoundMethod defines all the data related to a Go method that is +// bound to the Wails application +type BoundMethod struct { + ID uint32 `json:"id"` + Name string `json:"name"` + Inputs []*Parameter `json:"inputs,omitempty"` + Outputs []*Parameter `json:"outputs,omitempty"` + Comments string `json:"comments,omitempty"` + Method reflect.Value `json:"-"` + FQN string + + marshalError func(error) []byte + needsContext bool +} + +type Bindings struct { + marshalError func(error) []byte + boundMethods map[string]*BoundMethod + boundByID map[uint32]*BoundMethod + methodAliases map[uint32]uint32 +} + +func NewBindings(marshalError func(error) []byte, aliases map[uint32]uint32) *Bindings { + return &Bindings{ + marshalError: wrapErrorMarshaler(marshalError, defaultMarshalError), + boundMethods: make(map[string]*BoundMethod), + boundByID: make(map[uint32]*BoundMethod), + methodAliases: aliases, + } +} + +// Add adds the given service to the bindings. +func (b *Bindings) Add(service Service) error { + methods, err := getMethods(service.Instance()) + if err != nil { + return err + } + + marshalError := wrapErrorMarshaler(service.options.MarshalError, defaultMarshalError) + + // Validate and log methods. + for _, method := range methods { + if _, ok := b.boundMethods[method.FQN]; ok { + return fmt.Errorf("bound method '%s' is already registered. Please note that you can register at most one service of each type; additional instances must be wrapped in dedicated structs", method.FQN) + } + if boundMethod, ok := b.boundByID[method.ID]; ok { + return fmt.Errorf("oh wow, we're sorry about this! Amazingly, a hash collision was detected for method '%s' (it generates the same hash as '%s'). To use this method, please rename it. Sorry :(", method.FQN, boundMethod.FQN) + } + + // Log + attrs := []any{"fqn", method.FQN, "id", method.ID} + if alias, ok := lo.FindKey(b.methodAliases, method.ID); ok { + attrs = append(attrs, "alias", alias) + } + globalApplication.debug("Registering bound method:", attrs...) + } + + for _, method := range methods { + // Store composite error marshaler + method.marshalError = marshalError + + // Register method + b.boundMethods[method.FQN] = method + b.boundByID[method.ID] = method + } + + return nil +} + +// Get returns the bound method with the given name +func (b *Bindings) Get(options *CallOptions) *BoundMethod { + return b.boundMethods[options.MethodName] +} + +// GetByID returns the bound method with the given ID +func (b *Bindings) GetByID(id uint32) *BoundMethod { + // Check method aliases + if b.methodAliases != nil { + if alias, ok := b.methodAliases[id]; ok { + id = alias + } + } + + return b.boundByID[id] +} + +// internalServiceMethod is a set of methods +// that are handled specially by the binding engine +// and must not be exposed to the frontend. +// +// For simplicity we exclude these by name +// without checking their signatures, +// and so does the binding generator. +var internalServiceMethods = map[string]bool{ + "ServiceName": true, + "ServiceStartup": true, + "ServiceShutdown": true, + "ServeHTTP": true, +} + +var ctxType = reflect.TypeFor[context.Context]() + +func getMethods(value any) ([]*BoundMethod, error) { + // Create result placeholder + var result []*BoundMethod + + // Check type + if !isNamed(value) { + if isFunction(value) { + name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name() + return nil, fmt.Errorf("%s is a function, not a pointer to named type. Wails v2 has deprecated the binding of functions. Please define your functions as methods on a struct and bind a pointer to that struct", name) + } + + return nil, fmt.Errorf("%s is not a pointer to named type", reflect.ValueOf(value).Type().String()) + } else if !isPtr(value) { + return nil, fmt.Errorf("%s is a named type, not a pointer to named type", reflect.ValueOf(value).Type().String()) + } + + // Process Named Type + namedValue := reflect.ValueOf(value) + ptrType := namedValue.Type() + namedType := ptrType.Elem() + typeName := namedType.Name() + packagePath := namedType.PkgPath() + + if strings.Contains(namedType.String(), "[") { + return nil, fmt.Errorf("%s.%s is a generic type. Generic bound types are not supported", packagePath, namedType.String()) + } + + // Process Methods + for i := range ptrType.NumMethod() { + methodName := ptrType.Method(i).Name + method := namedValue.Method(i) + + if internalServiceMethods[methodName] { + continue + } + + fqn := fmt.Sprintf("%s.%s.%s", packagePath, typeName, methodName) + + // Create new method + boundMethod := &BoundMethod{ + ID: hash.Fnv(fqn), + FQN: fqn, + Name: methodName, + Inputs: nil, + Outputs: nil, + Comments: "", + Method: method, + } + + // Iterate inputs + methodType := method.Type() + inputParamCount := methodType.NumIn() + var inputs []*Parameter + for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ { + input := methodType.In(inputIndex) + if inputIndex == 0 && input.AssignableTo(ctxType) { + boundMethod.needsContext = true + } + thisParam := newParameter("", input) + inputs = append(inputs, thisParam) + } + + boundMethod.Inputs = inputs + + outputParamCount := methodType.NumOut() + var outputs []*Parameter + for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ { + output := methodType.Out(outputIndex) + thisParam := newParameter("", output) + outputs = append(outputs, thisParam) + } + boundMethod.Outputs = outputs + + // Save method in result + result = append(result, boundMethod) + + } + + return result, nil +} + +func (b *BoundMethod) String() string { + return b.FQN +} + +var errorType = reflect.TypeFor[error]() + +// Call will attempt to call this bound method with the given args. +// If the call succeeds, result will be either a non-error return value (if there is only one) +// or a slice of non-error return values (if there are more than one). +// +// If the arguments are mistyped or the call returns one or more non-nil error values, +// result is nil and err is an instance of *[CallError]. +func (b *BoundMethod) Call(ctx context.Context, args []json.RawMessage) (result any, err error) { + // Use a defer statement to capture panics + defer handlePanic(handlePanicOptions{skipEnd: 5}) + argCount := len(args) + if b.needsContext { + argCount++ + } + + if argCount != len(b.Inputs) { + err = &CallError{ + Kind: TypeError, + Message: fmt.Sprintf("%s expects %d arguments, got %d", b.FQN, len(b.Inputs), argCount), + } + return + } + + // Convert inputs to values of appropriate type + callArgs := make([]reflect.Value, argCount) + base := 0 + + if b.needsContext { + callArgs[0] = reflect.ValueOf(ctx) + base++ + } + + // Iterate over given arguments + for index, arg := range args { + value := reflect.New(b.Inputs[base+index].ReflectType) + err = json.Unmarshal(arg, value.Interface()) + if err != nil { + err = &CallError{ + Kind: TypeError, + Message: fmt.Sprintf("could not parse argument #%d: %s", index, err), + Cause: json.RawMessage(b.marshalError(err)), + } + return + } + callArgs[base+index] = value.Elem() + } + + // Do the call + var callResults []reflect.Value + if b.Method.Type().IsVariadic() { + callResults = b.Method.CallSlice(callArgs) + } else { + callResults = b.Method.Call(callArgs) + } + + var nonErrorOutputs = make([]any, 0, len(callResults)) + var errorOutputs []error + + for _, field := range callResults { + if field.Type() == errorType { + if field.IsNil() { + continue + } + if errorOutputs == nil { + errorOutputs = make([]error, 0, len(callResults)-len(nonErrorOutputs)) + nonErrorOutputs = nil + } + errorOutputs = append(errorOutputs, field.Interface().(error)) + } else if nonErrorOutputs != nil { + nonErrorOutputs = append(nonErrorOutputs, field.Interface()) + } + } + + if len(errorOutputs) > 0 { + info := make([]json.RawMessage, len(errorOutputs)) + for i, err := range errorOutputs { + info[i] = b.marshalError(err) + } + + cerr := &CallError{ + Kind: RuntimeError, + Message: errors.Join(errorOutputs...).Error(), + Cause: info, + } + if len(info) == 1 { + cerr.Cause = info[0] + } + + err = cerr + } else if len(nonErrorOutputs) == 1 { + result = nonErrorOutputs[0] + } else if len(nonErrorOutputs) > 1 { + result = nonErrorOutputs + } + + return +} + +// wrapErrorMarshaler returns an error marshaling functions +// that calls the primary marshaler first, +// then falls back to the secondary one. +func wrapErrorMarshaler(primary func(error) []byte, secondary func(error) []byte) func(error) []byte { + if primary == nil { + return secondary + } + + return func(err error) []byte { + result := primary(err) + if result == nil { + result = secondary(err) + } + + return result + } +} + +// defaultMarshalError implements the default error marshaling mechanism. +func defaultMarshalError(err error) []byte { + result, jsonErr := json.Marshal(&err) + if jsonErr != nil { + return nil + } + return result +} + +// isPtr returns true if the value given is a pointer. +func isPtr(value interface{}) bool { + return reflect.ValueOf(value).Kind() == reflect.Ptr +} + +// isFunction returns true if the given value is a function +func isFunction(value interface{}) bool { + return reflect.ValueOf(value).Kind() == reflect.Func +} + +// isNamed returns true if the given value is of named type +// or pointer to named type. +func isNamed(value interface{}) bool { + rv := reflect.ValueOf(value) + if rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + + return rv.Type().Name() != "" +} diff --git a/v3/pkg/application/bindings_test.go b/v3/pkg/application/bindings_test.go new file mode 100644 index 000000000..d76a8efe0 --- /dev/null +++ b/v3/pkg/application/bindings_test.go @@ -0,0 +1,191 @@ +package application_test + +import ( + "context" + "encoding/json" + "errors" + "reflect" + "strings" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type TestService struct { +} + +type Person struct { + Name string `json:"name"` +} + +func (t *TestService) Nil() {} + +func (t *TestService) String(s string) string { + return s +} + +func (t *TestService) Multiple(s string, i int, b bool) (string, int, bool) { + return s, i, b +} + +func (t *TestService) Struct(p Person) Person { + return p +} + +func (t *TestService) StructNil(p Person) (Person, error) { + return p, nil +} + +func (t *TestService) StructError(p Person) (Person, error) { + return p, errors.New("error") +} + +func (t *TestService) Variadic(s ...string) []string { + return s +} + +func (t *TestService) PositionalAndVariadic(a int, _ ...string) int { + return a +} + +func (t *TestService) Slice(a []int) []int { + return a +} + +func newArgs(jsonArgs ...string) (args []json.RawMessage) { + for _, j := range jsonArgs { + args = append(args, json.RawMessage(j)) + } + return +} + +func TestBoundMethodCall(t *testing.T) { + tests := []struct { + name string + method string + args []json.RawMessage + err string + expected interface{} + }{ + { + name: "nil", + method: "Nil", + args: []json.RawMessage{}, + err: "", + expected: nil, + }, + { + name: "string", + method: "String", + args: newArgs(`"foo"`), + err: "", + expected: "foo", + }, + { + name: "multiple", + method: "Multiple", + args: newArgs(`"foo"`, "0", "false"), + err: "", + expected: []interface{}{"foo", 0, false}, + }, + { + name: "struct", + method: "Struct", + args: newArgs(`{ "name": "alice" }`), + err: "", + expected: Person{Name: "alice"}, + }, + { + name: "struct, nil error", + method: "StructNil", + args: newArgs(`{ "name": "alice" }`), + err: "", + expected: Person{Name: "alice"}, + }, + { + name: "struct, error", + method: "StructError", + args: newArgs(`{ "name": "alice" }`), + err: "error", + expected: nil, + }, + { + name: "invalid argument count", + method: "Multiple", + args: newArgs(`"foo"`), + err: "expects 3 arguments, got 1", + expected: nil, + }, + { + name: "invalid argument type", + method: "String", + args: newArgs("1"), + err: "could not parse", + expected: nil, + }, + { + name: "variadic, no arguments", + method: "Variadic", + args: newArgs(`[]`), // variadic parameters are passed as arrays + err: "", + expected: []string{}, + }, + { + name: "variadic", + method: "Variadic", + args: newArgs(`["foo", "bar"]`), + err: "", + expected: []string{"foo", "bar"}, + }, + { + name: "positional and variadic", + method: "PositionalAndVariadic", + args: newArgs("42", `[]`), + err: "", + expected: 42, + }, + { + name: "slice", + method: "Slice", + args: newArgs(`[1,2,3]`), + err: "", + expected: []int{1, 2, 3}, + }, + } + + // init globalApplication + _ = application.New(application.Options{}) + + bindings := application.NewBindings(nil, nil) + + err := bindings.Add(application.NewService(&TestService{})) + if err != nil { + t.Fatalf("bindings.Add() error = %v\n", err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + callOptions := &application.CallOptions{ + MethodName: "github.com/wailsapp/wails/v3/pkg/application_test.TestService." + tt.method, + } + + method := bindings.Get(callOptions) + if method == nil { + t.Fatalf("bound method not found: %s", callOptions.MethodName) + } + + result, err := method.Call(context.TODO(), tt.args) + if (tt.err == "") != (err == nil) || (err != nil && !strings.Contains(err.Error(), tt.err)) { + expected := tt.err + if expected == "" { + expected = "nil" + } + t.Fatalf("error: %#v, expected error: %v", err, expected) + } + if !reflect.DeepEqual(result, tt.expected) { + t.Fatalf("result: %v, expected result: %v", result, tt.expected) + } + }) + } + +} diff --git a/v3/pkg/application/browser_manager.go b/v3/pkg/application/browser_manager.go new file mode 100644 index 000000000..3fe81720a --- /dev/null +++ b/v3/pkg/application/browser_manager.go @@ -0,0 +1,27 @@ +package application + +import ( + "github.com/pkg/browser" +) + +// BrowserManager manages browser-related operations +type BrowserManager struct { + app *App +} + +// newBrowserManager creates a new BrowserManager instance +func newBrowserManager(app *App) *BrowserManager { + return &BrowserManager{ + app: app, + } +} + +// OpenURL opens a URL in the default browser +func (bm *BrowserManager) OpenURL(url string) error { + return browser.OpenURL(url) +} + +// OpenFile opens a file in the default browser +func (bm *BrowserManager) OpenFile(path string) error { + return browser.OpenFile(path) +} diff --git a/v3/pkg/application/clipboard.go b/v3/pkg/application/clipboard.go new file mode 100644 index 000000000..f21b597e2 --- /dev/null +++ b/v3/pkg/application/clipboard.go @@ -0,0 +1,26 @@ +package application + +type clipboardImpl interface { + setText(text string) bool + text() (string, bool) +} + +type Clipboard struct { + impl clipboardImpl +} + +func newClipboard() *Clipboard { + return &Clipboard{ + impl: newClipboardImpl(), + } +} + +func (c *Clipboard) SetText(text string) bool { + return InvokeSyncWithResult(func() bool { + return c.impl.setText(text) + }) +} + +func (c *Clipboard) Text() (string, bool) { + return InvokeSyncWithResultAndOther(c.impl.text) +} diff --git a/v3/pkg/application/clipboard_darwin.go b/v3/pkg/application/clipboard_darwin.go new file mode 100644 index 000000000..3c0c80ac6 --- /dev/null +++ b/v3/pkg/application/clipboard_darwin.go @@ -0,0 +1,56 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa -mmacosx-version-min=10.13 + +#import +#import + +bool setClipboardText(const char* text) { + NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; + NSError *error = nil; + NSString *string = [NSString stringWithUTF8String:text]; + [pasteBoard clearContents]; + return [pasteBoard setString:string forType:NSPasteboardTypeString]; +} + +const char* getClipboardText() { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSString *text = [pasteboard stringForType:NSPasteboardTypeString]; + return [text UTF8String]; +} + +*/ +import "C" +import ( + "sync" + "unsafe" +) + +var clipboardLock sync.RWMutex + +type macosClipboard struct{} + +func (m macosClipboard) setText(text string) bool { + clipboardLock.Lock() + defer clipboardLock.Unlock() + cText := C.CString(text) + success := C.setClipboardText(cText) + C.free(unsafe.Pointer(cText)) + return bool(success) +} + +func (m macosClipboard) text() (string, bool) { + clipboardLock.RLock() + defer clipboardLock.RUnlock() + clipboardText := C.getClipboardText() + result := C.GoString(clipboardText) + return result, true +} + +func newClipboardImpl() *macosClipboard { + return &macosClipboard{} +} diff --git a/v3/pkg/application/clipboard_linux.go b/v3/pkg/application/clipboard_linux.go new file mode 100644 index 000000000..1c662cd6f --- /dev/null +++ b/v3/pkg/application/clipboard_linux.go @@ -0,0 +1,28 @@ +//go:build linux + +package application + +import ( + "sync" +) + +var clipboardLock sync.RWMutex + +type linuxClipboard struct{} + +func (m linuxClipboard) setText(text string) bool { + clipboardLock.Lock() + defer clipboardLock.Unlock() + clipboardSet(text) + return true +} + +func (m linuxClipboard) text() (string, bool) { + clipboardLock.RLock() + defer clipboardLock.RUnlock() + return clipboardGet(), true +} + +func newClipboardImpl() *linuxClipboard { + return &linuxClipboard{} +} diff --git a/v3/pkg/application/clipboard_manager.go b/v3/pkg/application/clipboard_manager.go new file mode 100644 index 000000000..abd0b19c1 --- /dev/null +++ b/v3/pkg/application/clipboard_manager.go @@ -0,0 +1,32 @@ +package application + +// ClipboardManager manages clipboard operations +type ClipboardManager struct { + app *App + clipboard *Clipboard +} + +// newClipboardManager creates a new ClipboardManager instance +func newClipboardManager(app *App) *ClipboardManager { + return &ClipboardManager{ + app: app, + } +} + +// SetText sets text in the clipboard +func (cm *ClipboardManager) SetText(text string) bool { + return cm.getClipboard().SetText(text) +} + +// Text gets text from the clipboard +func (cm *ClipboardManager) Text() (string, bool) { + return cm.getClipboard().Text() +} + +// getClipboard returns the clipboard instance, creating it if needed (lazy initialization) +func (cm *ClipboardManager) getClipboard() *Clipboard { + if cm.clipboard == nil { + cm.clipboard = newClipboard() + } + return cm.clipboard +} diff --git a/v3/pkg/application/clipboard_windows.go b/v3/pkg/application/clipboard_windows.go new file mode 100644 index 000000000..507eac3da --- /dev/null +++ b/v3/pkg/application/clipboard_windows.go @@ -0,0 +1,29 @@ +//go:build windows + +package application + +import ( + "github.com/wailsapp/wails/v3/pkg/w32" + "sync" +) + +type windowsClipboard struct { + lock sync.RWMutex +} + +func (m *windowsClipboard) setText(text string) bool { + m.lock.Lock() + defer m.lock.Unlock() + return w32.SetClipboardText(text) == nil +} + +func (m *windowsClipboard) text() (string, bool) { + m.lock.Lock() + defer m.lock.Unlock() + text, err := w32.GetClipboardText() + return text, err == nil +} + +func newClipboardImpl() *windowsClipboard { + return &windowsClipboard{} +} diff --git a/v3/pkg/application/context.go b/v3/pkg/application/context.go new file mode 100644 index 000000000..8ed2cb792 --- /dev/null +++ b/v3/pkg/application/context.go @@ -0,0 +1,62 @@ +package application + +type Context struct { + // contains filtered or unexported fields + data map[string]any +} + +func newContext() *Context { + return &Context{ + data: make(map[string]any), + } +} + +const ( + clickedMenuItem string = "clickedMenuItem" + menuItemIsChecked string = "menuItemIsChecked" + contextMenuData string = "contextMenuData" +) + +func (c *Context) ClickedMenuItem() *MenuItem { + result, exists := c.data[clickedMenuItem] + if !exists { + return nil + } + return result.(*MenuItem) +} + +func (c *Context) IsChecked() bool { + result, exists := c.data[menuItemIsChecked] + if !exists { + return false + } + return result.(bool) +} +func (c *Context) ContextMenuData() string { + result := c.data[contextMenuData] + if result == nil { + return "" + } + str, ok := result.(string) + if !ok { + return "" + } + return str +} + +func (c *Context) withClickedMenuItem(menuItem *MenuItem) *Context { + c.data[clickedMenuItem] = menuItem + return c +} + +func (c *Context) withChecked(checked bool) { + c.data[menuItemIsChecked] = checked +} + +func (c *Context) withContextMenuData(data *ContextMenuData) *Context { + if data == nil { + return c + } + c.data[contextMenuData] = data.Data + return c +} diff --git a/v3/pkg/application/context_application_event.go b/v3/pkg/application/context_application_event.go new file mode 100644 index 000000000..32f392455 --- /dev/null +++ b/v3/pkg/application/context_application_event.go @@ -0,0 +1,106 @@ +package application + +import "log" + +var blankApplicationEventContext = &ApplicationEventContext{} + +const ( + CONTEXT_OPENED_FILES = "openedFiles" + CONTEXT_FILENAME = "filename" + CONTEXT_URL = "url" +) + +// ApplicationEventContext is the context of an application event +type ApplicationEventContext struct { + // contains filtered or unexported fields + data map[string]any +} + +// OpenedFiles returns the opened files from the event context if it was set +func (c ApplicationEventContext) OpenedFiles() []string { + files, ok := c.data[CONTEXT_OPENED_FILES] + if !ok { + return nil + } + result, ok := files.([]string) + if !ok { + return nil + } + return result +} + +func (c ApplicationEventContext) setOpenedFiles(files []string) { + c.data[CONTEXT_OPENED_FILES] = files +} + +func (c ApplicationEventContext) setIsDarkMode(mode bool) { + c.data["isDarkMode"] = mode +} + +func (c ApplicationEventContext) getBool(key string) bool { + mode, ok := c.data[key] + if !ok { + return false + } + result, ok := mode.(bool) + if !ok { + return false + } + return result +} + +// IsDarkMode returns true if the event context has a dark mode +func (c ApplicationEventContext) IsDarkMode() bool { + return c.getBool("isDarkMode") +} + +// HasVisibleWindows returns true if the event context has a visible window +func (c ApplicationEventContext) HasVisibleWindows() bool { + return c.getBool("hasVisibleWindows") +} + +func (c *ApplicationEventContext) setData(data map[string]any) { + c.data = data +} + +func (c *ApplicationEventContext) setOpenedWithFile(filepath string) { + c.data[CONTEXT_FILENAME] = filepath +} + +func (c *ApplicationEventContext) setURL(openedWithURL string) { + c.data[CONTEXT_URL] = openedWithURL +} + +// Filename returns the filename from the event context if it was set +func (c ApplicationEventContext) Filename() string { + filename, ok := c.data[CONTEXT_FILENAME] + if !ok { + return "" + } + result, ok := filename.(string) + if !ok { + return "" + } + return result +} + +// URL returns the URL from the event context if it was set +func (c ApplicationEventContext) URL() string { + url, ok := c.data[CONTEXT_URL] + if !ok { + log.Println("URL not found in event context") + return "" + } + result, ok := url.(string) + if !ok { + log.Println("URL not a string in event context") + return "" + } + return result +} + +func newApplicationEventContext() *ApplicationEventContext { + return &ApplicationEventContext{ + data: make(map[string]any), + } +} diff --git a/v3/pkg/application/context_menu_manager.go b/v3/pkg/application/context_menu_manager.go new file mode 100644 index 000000000..669e8aca5 --- /dev/null +++ b/v3/pkg/application/context_menu_manager.go @@ -0,0 +1,54 @@ +package application + +// ContextMenuManager manages all context menu operations +type ContextMenuManager struct { + app *App +} + +// newContextMenuManager creates a new ContextMenuManager instance +func newContextMenuManager(app *App) *ContextMenuManager { + return &ContextMenuManager{ + app: app, + } +} + +// New creates a new context menu +func (cmm *ContextMenuManager) New() *ContextMenu { + return &ContextMenu{ + Menu: NewMenu(), + } +} + +// Add adds a context menu (replaces Register for consistency) +func (cmm *ContextMenuManager) Add(name string, menu *ContextMenu) { + cmm.app.contextMenusLock.Lock() + defer cmm.app.contextMenusLock.Unlock() + cmm.app.contextMenus[name] = menu +} + +// Remove removes a context menu by name (replaces Unregister for consistency) +func (cmm *ContextMenuManager) Remove(name string) { + cmm.app.contextMenusLock.Lock() + defer cmm.app.contextMenusLock.Unlock() + delete(cmm.app.contextMenus, name) +} + +// Get retrieves a context menu by name +func (cmm *ContextMenuManager) Get(name string) (*ContextMenu, bool) { + cmm.app.contextMenusLock.RLock() + defer cmm.app.contextMenusLock.RUnlock() + menu, exists := cmm.app.contextMenus[name] + return menu, exists +} + +// GetAll returns all registered context menus as a slice +func (cmm *ContextMenuManager) GetAll() []*ContextMenu { + cmm.app.contextMenusLock.RLock() + defer cmm.app.contextMenusLock.RUnlock() + + result := make([]*ContextMenu, 0, len(cmm.app.contextMenus)) + for _, menu := range cmm.app.contextMenus { + result = append(result, menu) + } + return result +} diff --git a/v3/pkg/application/context_window_event.go b/v3/pkg/application/context_window_event.go new file mode 100644 index 000000000..a40ff9d48 --- /dev/null +++ b/v3/pkg/application/context_window_event.go @@ -0,0 +1,81 @@ +package application + +var blankWindowEventContext = &WindowEventContext{} + +const ( + droppedFiles = "droppedFiles" + dropZoneDetailsKey = "dropZoneDetails" +) + +type WindowEventContext struct { + // contains filtered or unexported fields + data map[string]any +} + +func (c WindowEventContext) DroppedFiles() []string { + if c.data == nil { + c.data = make(map[string]any) + } + files, ok := c.data[droppedFiles] + if !ok { + return nil + } + result, ok := files.([]string) + if !ok { + return nil + } + return result +} + +func (c WindowEventContext) setDroppedFiles(files []string) { + if c.data == nil { + c.data = make(map[string]any) + } + c.data[droppedFiles] = files +} + +func (c WindowEventContext) setCoordinates(x, y int) { + if c.data == nil { + c.data = make(map[string]any) + } + c.data["x"] = x + c.data["y"] = y +} + +func (c WindowEventContext) setDropZoneDetails(details *DropZoneDetails) { + if c.data == nil { + c.data = make(map[string]any) + } + if details == nil { + c.data[dropZoneDetailsKey] = nil + return + } + c.data[dropZoneDetailsKey] = details +} + +// DropZoneDetails retrieves the detailed drop zone information, if available. +func (c WindowEventContext) DropZoneDetails() *DropZoneDetails { + if c.data == nil { + c.data = make(map[string]any) + } + details, ok := c.data[dropZoneDetailsKey] + if !ok { + return nil + } + // Explicitly type assert, handle if it's nil (though setDropZoneDetails should handle it) + if details == nil { + return nil + } + result, ok := details.(*DropZoneDetails) + if !ok { + // This case indicates a programming error if data was set incorrectly + return nil + } + return result +} + +func newWindowEventContext() *WindowEventContext { + return &WindowEventContext{ + data: make(map[string]any), + } +} diff --git a/v3/pkg/application/dialog_manager.go b/v3/pkg/application/dialog_manager.go new file mode 100644 index 000000000..fa49392ae --- /dev/null +++ b/v3/pkg/application/dialog_manager.go @@ -0,0 +1,57 @@ +package application + +// DialogManager manages dialog-related operations +type DialogManager struct { + app *App +} + +// newDialogManager creates a new DialogManager instance +func newDialogManager(app *App) *DialogManager { + return &DialogManager{ + app: app, + } +} + +// OpenFile creates a file dialog for selecting files +func (dm *DialogManager) OpenFile() *OpenFileDialogStruct { + return OpenFileDialog() +} + +// OpenFileWithOptions creates a file dialog with options +func (dm *DialogManager) OpenFileWithOptions(options *OpenFileDialogOptions) *OpenFileDialogStruct { + result := OpenFileDialog() + result.SetOptions(options) + return result +} + +// SaveFile creates a save file dialog +func (dm *DialogManager) SaveFile() *SaveFileDialogStruct { + return SaveFileDialog() +} + +// SaveFileWithOptions creates a save file dialog with options +func (dm *DialogManager) SaveFileWithOptions(options *SaveFileDialogOptions) *SaveFileDialogStruct { + result := SaveFileDialog() + result.SetOptions(options) + return result +} + +// Info creates an information dialog +func (dm *DialogManager) Info() *MessageDialog { + return InfoDialog() +} + +// Question creates a question dialog +func (dm *DialogManager) Question() *MessageDialog { + return QuestionDialog() +} + +// Warning creates a warning dialog +func (dm *DialogManager) Warning() *MessageDialog { + return WarningDialog() +} + +// Error creates an error dialog +func (dm *DialogManager) Error() *MessageDialog { + return ErrorDialog() +} diff --git a/v3/pkg/application/dialogs.go b/v3/pkg/application/dialogs.go new file mode 100644 index 000000000..d61f64886 --- /dev/null +++ b/v3/pkg/application/dialogs.go @@ -0,0 +1,494 @@ +package application + +import ( + "strings" + "sync" +) + +type DialogType int + +var dialogMapID = make(map[uint]struct{}) +var dialogIDLock sync.RWMutex + +func getDialogID() uint { + dialogIDLock.Lock() + defer dialogIDLock.Unlock() + var dialogID uint + for { + if _, ok := dialogMapID[dialogID]; !ok { + dialogMapID[dialogID] = struct{}{} + break + } + dialogID++ + if dialogID == 0 { + panic("no more dialog IDs") + } + } + return dialogID +} + +func freeDialogID(id uint) { + dialogIDLock.Lock() + defer dialogIDLock.Unlock() + delete(dialogMapID, id) +} + +var openFileResponses = make(map[uint]chan string) +var saveFileResponses = make(map[uint]chan string) + +const ( + InfoDialogType DialogType = iota + QuestionDialogType + WarningDialogType + ErrorDialogType +) + +type Button struct { + Label string + IsCancel bool + IsDefault bool + Callback func() +} + +func (b *Button) OnClick(callback func()) *Button { + b.Callback = callback + return b +} + +func (b *Button) SetAsDefault() *Button { + b.IsDefault = true + return b +} + +func (b *Button) SetAsCancel() *Button { + b.IsCancel = true + return b +} + +type messageDialogImpl interface { + show() +} + +type MessageDialogOptions struct { + DialogType DialogType + Title string + Message string + Buttons []*Button + Icon []byte + window Window +} + +type MessageDialog struct { + MessageDialogOptions + + // platform independent + impl messageDialogImpl +} + +var defaultTitles = map[DialogType]string{ + InfoDialogType: "Information", + QuestionDialogType: "Question", + WarningDialogType: "Warning", + ErrorDialogType: "Error", +} + +func newMessageDialog(dialogType DialogType) *MessageDialog { + return &MessageDialog{ + MessageDialogOptions: MessageDialogOptions{ + DialogType: dialogType, + }, + impl: nil, + } +} + +func (d *MessageDialog) SetTitle(title string) *MessageDialog { + d.Title = title + return d +} + +func (d *MessageDialog) Show() { + if d.impl == nil { + d.impl = newDialogImpl(d) + } + InvokeSync(d.impl.show) +} + +func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog { + d.Icon = icon + return d +} + +func (d *MessageDialog) AddButton(s string) *Button { + result := &Button{ + Label: s, + } + d.Buttons = append(d.Buttons, result) + return result +} + +func (d *MessageDialog) AddButtons(buttons []*Button) *MessageDialog { + d.Buttons = buttons + return d +} + +func (d *MessageDialog) AttachToWindow(window Window) *MessageDialog { + d.window = window + return d +} + +func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog { + for _, b := range d.Buttons { + b.IsDefault = false + } + button.IsDefault = true + return d +} + +func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog { + for _, b := range d.Buttons { + b.IsCancel = false + } + button.IsCancel = true + return d +} + +func (d *MessageDialog) SetMessage(message string) *MessageDialog { + d.Message = message + return d +} + +type openFileDialogImpl interface { + show() (chan string, error) +} + +type FileFilter struct { + DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)" + Pattern string // semicolon separated list of extensions, EG: "*.jpg;*.png" +} + +type OpenFileDialogOptions struct { + CanChooseDirectories bool + CanChooseFiles bool + CanCreateDirectories bool + ShowHiddenFiles bool + ResolvesAliases bool + AllowsMultipleSelection bool + HideExtension bool + CanSelectHiddenExtension bool + TreatsFilePackagesAsDirectories bool + AllowsOtherFileTypes bool + Filters []FileFilter + Window Window + + Title string + Message string + ButtonText string + Directory string +} + +type OpenFileDialogStruct struct { + id uint + canChooseDirectories bool + canChooseFiles bool + canCreateDirectories bool + showHiddenFiles bool + resolvesAliases bool + allowsMultipleSelection bool + hideExtension bool + canSelectHiddenExtension bool + treatsFilePackagesAsDirectories bool + allowsOtherFileTypes bool + filters []FileFilter + + title string + message string + buttonText string + directory string + window Window + + impl openFileDialogImpl +} + +func (d *OpenFileDialogStruct) CanChooseFiles(canChooseFiles bool) *OpenFileDialogStruct { + d.canChooseFiles = canChooseFiles + return d +} + +func (d *OpenFileDialogStruct) CanChooseDirectories(canChooseDirectories bool) *OpenFileDialogStruct { + d.canChooseDirectories = canChooseDirectories + return d +} + +func (d *OpenFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *OpenFileDialogStruct { + d.canCreateDirectories = canCreateDirectories + return d +} + +func (d *OpenFileDialogStruct) AllowsOtherFileTypes(allowsOtherFileTypes bool) *OpenFileDialogStruct { + d.allowsOtherFileTypes = allowsOtherFileTypes + return d +} + +func (d *OpenFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *OpenFileDialogStruct { + d.showHiddenFiles = showHiddenFiles + return d +} + +func (d *OpenFileDialogStruct) HideExtension(hideExtension bool) *OpenFileDialogStruct { + d.hideExtension = hideExtension + return d +} + +func (d *OpenFileDialogStruct) TreatsFilePackagesAsDirectories(treatsFilePackagesAsDirectories bool) *OpenFileDialogStruct { + d.treatsFilePackagesAsDirectories = treatsFilePackagesAsDirectories + return d +} + +func (d *OpenFileDialogStruct) AttachToWindow(window Window) *OpenFileDialogStruct { + d.window = window + return d +} + +func (d *OpenFileDialogStruct) ResolvesAliases(resolvesAliases bool) *OpenFileDialogStruct { + d.resolvesAliases = resolvesAliases + return d +} + +func (d *OpenFileDialogStruct) SetTitle(title string) *OpenFileDialogStruct { + d.title = title + return d +} + +func (d *OpenFileDialogStruct) PromptForSingleSelection() (string, error) { + d.allowsMultipleSelection = false + if d.impl == nil { + d.impl = newOpenFileDialogImpl(d) + } + + var result string + selections, err := InvokeSyncWithResultAndError(d.impl.show) + if err == nil { + result = <-selections + } + + return result, err +} + +// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions. +// EG: AddFilter("Image Files", "*.jpg;*.png") +func (d *OpenFileDialogStruct) AddFilter(displayName, pattern string) *OpenFileDialogStruct { + d.filters = append(d.filters, FileFilter{ + DisplayName: strings.TrimSpace(displayName), + Pattern: strings.TrimSpace(pattern), + }) + return d +} + +func (d *OpenFileDialogStruct) PromptForMultipleSelection() ([]string, error) { + d.allowsMultipleSelection = true + if d.impl == nil { + d.impl = newOpenFileDialogImpl(d) + } + + selections, err := InvokeSyncWithResultAndError(d.impl.show) + if err != nil { + return nil, err + } + + var result []string + for filename := range selections { + result = append(result, filename) + } + + return result, err +} + +func (d *OpenFileDialogStruct) SetMessage(message string) *OpenFileDialogStruct { + d.message = message + return d +} + +func (d *OpenFileDialogStruct) SetButtonText(text string) *OpenFileDialogStruct { + d.buttonText = text + return d +} + +func (d *OpenFileDialogStruct) SetDirectory(directory string) *OpenFileDialogStruct { + d.directory = directory + return d +} + +func (d *OpenFileDialogStruct) CanSelectHiddenExtension(canSelectHiddenExtension bool) *OpenFileDialogStruct { + d.canSelectHiddenExtension = canSelectHiddenExtension + return d +} + +func (d *OpenFileDialogStruct) SetOptions(options *OpenFileDialogOptions) { + d.title = options.Title + d.message = options.Message + d.buttonText = options.ButtonText + d.directory = options.Directory + d.canChooseDirectories = options.CanChooseDirectories + d.canChooseFiles = options.CanChooseFiles + d.canCreateDirectories = options.CanCreateDirectories + d.showHiddenFiles = options.ShowHiddenFiles + d.resolvesAliases = options.ResolvesAliases + d.allowsMultipleSelection = options.AllowsMultipleSelection + d.hideExtension = options.HideExtension + d.canSelectHiddenExtension = options.CanSelectHiddenExtension + d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories + d.allowsOtherFileTypes = options.AllowsOtherFileTypes + d.filters = options.Filters + d.window = options.Window +} + +func newOpenFileDialog() *OpenFileDialogStruct { + return &OpenFileDialogStruct{ + id: getDialogID(), + canChooseDirectories: false, + canChooseFiles: true, + canCreateDirectories: true, + resolvesAliases: false, + } +} + +func newSaveFileDialog() *SaveFileDialogStruct { + return &SaveFileDialogStruct{ + id: getDialogID(), + canCreateDirectories: true, + } +} + +type SaveFileDialogOptions struct { + CanCreateDirectories bool + ShowHiddenFiles bool + CanSelectHiddenExtension bool + AllowOtherFileTypes bool + HideExtension bool + TreatsFilePackagesAsDirectories bool + Title string + Message string + Directory string + Filename string + ButtonText string + Filters []FileFilter + Window Window +} + +type SaveFileDialogStruct struct { + id uint + canCreateDirectories bool + showHiddenFiles bool + canSelectHiddenExtension bool + allowOtherFileTypes bool + hideExtension bool + treatsFilePackagesAsDirectories bool + message string + directory string + filename string + buttonText string + filters []FileFilter + + window Window + + impl saveFileDialogImpl + title string +} + +type saveFileDialogImpl interface { + show() (chan string, error) +} + +func (d *SaveFileDialogStruct) SetOptions(options *SaveFileDialogOptions) { + d.title = options.Title + d.canCreateDirectories = options.CanCreateDirectories + d.showHiddenFiles = options.ShowHiddenFiles + d.canSelectHiddenExtension = options.CanSelectHiddenExtension + d.allowOtherFileTypes = options.AllowOtherFileTypes + d.hideExtension = options.HideExtension + d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories + d.message = options.Message + d.directory = options.Directory + d.filename = options.Filename + d.buttonText = options.ButtonText + d.filters = options.Filters + d.window = options.Window +} + +// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions. +// EG: AddFilter("Image Files", "*.jpg;*.png") +func (d *SaveFileDialogStruct) AddFilter(displayName, pattern string) *SaveFileDialogStruct { + d.filters = append(d.filters, FileFilter{ + DisplayName: strings.TrimSpace(displayName), + Pattern: strings.TrimSpace(pattern), + }) + return d +} + +func (d *SaveFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *SaveFileDialogStruct { + d.canCreateDirectories = canCreateDirectories + return d +} + +func (d *SaveFileDialogStruct) CanSelectHiddenExtension(canSelectHiddenExtension bool) *SaveFileDialogStruct { + d.canSelectHiddenExtension = canSelectHiddenExtension + return d +} + +func (d *SaveFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *SaveFileDialogStruct { + d.showHiddenFiles = showHiddenFiles + return d +} + +func (d *SaveFileDialogStruct) SetMessage(message string) *SaveFileDialogStruct { + d.message = message + return d +} + +func (d *SaveFileDialogStruct) SetDirectory(directory string) *SaveFileDialogStruct { + d.directory = directory + return d +} + +func (d *SaveFileDialogStruct) AttachToWindow(window Window) *SaveFileDialogStruct { + d.window = window + return d +} + +func (d *SaveFileDialogStruct) PromptForSingleSelection() (string, error) { + if d.impl == nil { + d.impl = newSaveFileDialogImpl(d) + } + + var result string + selections, err := InvokeSyncWithResultAndError(d.impl.show) + if err == nil { + result = <-selections + } + return result, err +} + +func (d *SaveFileDialogStruct) SetButtonText(text string) *SaveFileDialogStruct { + d.buttonText = text + return d +} + +func (d *SaveFileDialogStruct) SetFilename(filename string) *SaveFileDialogStruct { + d.filename = filename + return d +} + +func (d *SaveFileDialogStruct) AllowsOtherFileTypes(allowOtherFileTypes bool) *SaveFileDialogStruct { + d.allowOtherFileTypes = allowOtherFileTypes + return d +} + +func (d *SaveFileDialogStruct) HideExtension(hideExtension bool) *SaveFileDialogStruct { + d.hideExtension = hideExtension + return d +} + +func (d *SaveFileDialogStruct) TreatsFilePackagesAsDirectories(treatsFilePackagesAsDirectories bool) *SaveFileDialogStruct { + d.treatsFilePackagesAsDirectories = treatsFilePackagesAsDirectories + return d +} diff --git a/v3/pkg/application/dialogs_darwin.go b/v3/pkg/application/dialogs_darwin.go new file mode 100644 index 000000000..f8e324595 --- /dev/null +++ b/v3/pkg/application/dialogs_darwin.go @@ -0,0 +1,588 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa -mmacosx-version-min=10.13 -framework UniformTypeIdentifiers + +#import + +#import +#import "dialogs_darwin_delegate.h" + +extern void openFileDialogCallback(uint id, char* path); +extern void openFileDialogCallbackEnd(uint id); +extern void saveFileDialogCallback(uint id, char* path); +extern void dialogCallback(int id, int buttonPressed); + +static void showAboutBox(char* title, char *message, void *icon, int length) { + + // run on main thread + NSAlert *alert = [[NSAlert alloc] init]; + if (title != NULL) { + [alert setMessageText:[NSString stringWithUTF8String:title]]; + free(title); + } + if (message != NULL) { + [alert setInformativeText:[NSString stringWithUTF8String:message]]; + free(message); + } + if (icon != NULL) { + NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithBytes:icon length:length]]; + [alert setIcon:image]; + } + [alert setAlertStyle:NSAlertStyleInformational]; + [alert runModal]; +} + + +// Create an NSAlert +static void* createAlert(int alertType, char* title, char *message, void *icon, int length) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setAlertStyle:alertType]; + if (title != NULL) { + [alert setMessageText:[NSString stringWithUTF8String:title]]; + free(title); + } + if (message != NULL) { + [alert setInformativeText:[NSString stringWithUTF8String:message]]; + free(message); + } + if (icon != NULL) { + NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithBytes:icon length:length]]; + [alert setIcon:image]; + } else { + if(alertType == NSAlertStyleCritical || alertType == NSAlertStyleWarning) { + NSImage *image = [NSImage imageNamed:NSImageNameCaution]; + [alert setIcon:image]; + } else { + NSImage *image = [NSImage imageNamed:NSImageNameInfo]; + [alert setIcon:image]; + } + } + return alert; + +} + +static int getButtonNumber(NSModalResponse response) { + int buttonNumber = 0; + if( response == NSAlertFirstButtonReturn ) { + buttonNumber = 0; + } + else if( response == NSAlertSecondButtonReturn ) { + buttonNumber = 1; + } + else if( response == NSAlertThirdButtonReturn ) { + buttonNumber = 2; + } else { + buttonNumber = 3; + } + return buttonNumber; +} + +// Run the dialog +static void dialogRunModal(void *dialog, void *parent, int callBackID) { + NSAlert *alert = (__bridge NSAlert *)dialog; + + // If the parent is NULL, we are running a modal dialog, otherwise attach the alert to the parent + if( parent == NULL ) { + NSModalResponse response = [alert runModal]; + int returnCode = getButtonNumber(response); + dialogCallback(callBackID, returnCode); + } else { + NSWindow *window = (__bridge NSWindow *)parent; + [alert beginSheetModalForWindow:window completionHandler:^(NSModalResponse response) { + int returnCode = getButtonNumber(response); + dialogCallback(callBackID, returnCode); + }]; + } +} + +// Release the dialog +static void releaseDialog(void *dialog) { + NSAlert *alert = (__bridge NSAlert *)dialog; + [alert release]; +} + +// Add a button to the dialog +static void alertAddButton(void *dialog, char *label, bool isDefault, bool isCancel) { + NSAlert *alert = (__bridge NSAlert *)dialog; + NSButton *button = [alert addButtonWithTitle:[NSString stringWithUTF8String:label]]; + free(label); + if( isDefault ) { + [button setKeyEquivalent:@"\r"]; + } else if( isCancel ) { + [button setKeyEquivalent:@"\033"]; + } else { + [button setKeyEquivalent:@""]; + } +} + +static void processOpenFileDialogResults(NSOpenPanel *panel, NSInteger result, uint dialogID) { + const char *path = NULL; + if (result == NSModalResponseOK) { + NSArray *urls = [panel URLs]; + if ([urls count] > 0) { + NSArray *urls = [panel URLs]; + for (NSURL *url in urls) { + path = [[url path] UTF8String]; + openFileDialogCallback(dialogID, (char *)path); + } + } else { + NSURL *url = [panel URL]; + path = [[url path] UTF8String]; + openFileDialogCallback(dialogID, (char *)path); + } + } + openFileDialogCallbackEnd(dialogID); +} + + +static void showOpenFileDialog(unsigned int dialogID, + bool canChooseFiles, + bool canChooseDirectories, + bool canCreateDirectories, + bool showHiddenFiles, + bool allowsMultipleSelection, + bool resolvesAliases, + bool hideExtension, + bool treatsFilePackagesAsDirectories, + bool allowsOtherFileTypes, + char *filterPatterns, + unsigned int filterPatternsCount, + char* message, + char* directory, + char* buttonText, + + void *window) { + + // run on main thread + NSOpenPanel *panel = [NSOpenPanel openPanel]; + + // print out filterPatterns if length > 0 + if (filterPatternsCount > 0) { + OpenPanelDelegate *delegate = [[OpenPanelDelegate alloc] init]; + [panel setDelegate:delegate]; + // Initialise NSString with bytes and UTF8 encoding + NSString *filterPatternsString = [[NSString alloc] initWithBytes:filterPatterns length:filterPatternsCount encoding:NSUTF8StringEncoding]; + // Convert NSString to NSArray + delegate.allowedExtensions = [filterPatternsString componentsSeparatedByString:@";"]; + + // Use UTType if macOS 11 or higher to add file filters +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + if (@available(macOS 11, *)) { + NSMutableArray *filterTypes = [NSMutableArray array]; + // Iterate the filtertypes, create uti's that are limited to the file extensions then add + for (NSString *filterType in delegate.allowedExtensions) { + [filterTypes addObject:[UTType typeWithFilenameExtension:filterType]]; + } + [panel setAllowedContentTypes:filterTypes]; + } +#else + [panel setAllowedFileTypes:delegate.allowedExtensions]; +#endif + + // Free the memory + free(filterPatterns); + } + + + if (message != NULL) { + [panel setMessage:[NSString stringWithUTF8String:message]]; + free(message); + } + + if (directory != NULL) { + [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:directory]]]; + free(directory); + } + + if (buttonText != NULL) { + [panel setPrompt:[NSString stringWithUTF8String:buttonText]]; + free(buttonText); + } + + [panel setCanChooseFiles:canChooseFiles]; + [panel setCanChooseDirectories:canChooseDirectories]; + [panel setCanCreateDirectories:canCreateDirectories]; + [panel setShowsHiddenFiles:showHiddenFiles]; + [panel setAllowsMultipleSelection:allowsMultipleSelection]; + [panel setResolvesAliases:resolvesAliases]; + [panel setExtensionHidden:hideExtension]; + [panel setTreatsFilePackagesAsDirectories:treatsFilePackagesAsDirectories]; + [panel setAllowsOtherFileTypes:allowsOtherFileTypes]; + + + + if (window != NULL) { + [panel beginSheetModalForWindow:(__bridge NSWindow *)window completionHandler:^(NSInteger result) { + processOpenFileDialogResults(panel, result, dialogID); + }]; + } else { + [panel beginWithCompletionHandler:^(NSInteger result) { + processOpenFileDialogResults(panel, result, dialogID); + }]; + } +} + +static void showSaveFileDialog(unsigned int dialogID, + bool canCreateDirectories, + bool showHiddenFiles, + bool canSelectHiddenExtension, + bool hideExtension, + bool treatsFilePackagesAsDirectories, + bool allowOtherFileTypes, + char* message, + char* directory, + char* buttonText, + char* filename, + void *window) { + + NSSavePanel *panel = [NSSavePanel savePanel]; + + if (message != NULL) { + [panel setMessage:[NSString stringWithUTF8String:message]]; + free(message); + } + + if (directory != NULL) { + [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:directory]]]; + free(directory); + } + + if (filename != NULL) { + [panel setNameFieldStringValue:[NSString stringWithUTF8String:filename]]; + free(filename); + } + + if (buttonText != NULL) { + [panel setPrompt:[NSString stringWithUTF8String:buttonText]]; + free(buttonText); + } + + [panel setCanCreateDirectories:canCreateDirectories]; + [panel setShowsHiddenFiles:showHiddenFiles]; + [panel setCanSelectHiddenExtension:canSelectHiddenExtension]; + [panel setExtensionHidden:hideExtension]; + [panel setTreatsFilePackagesAsDirectories:treatsFilePackagesAsDirectories]; + [panel setAllowsOtherFileTypes:allowOtherFileTypes]; + + if (window != NULL) { + [panel beginSheetModalForWindow:(__bridge NSWindow *)window completionHandler:^(NSInteger result) { + const char *path = NULL; + if (result == NSModalResponseOK) { + NSURL *url = [panel URL]; + path = [[url path] UTF8String]; + } + saveFileDialogCallback(dialogID, (char *)path); + }]; + } else { + [panel beginWithCompletionHandler:^(NSInteger result) { + const char *path = NULL; + if (result == NSModalResponseOK) { + NSURL *url = [panel URL]; + path = [[url path] UTF8String]; + } + saveFileDialogCallback(dialogID, (char *)path); + }]; + } +} + +*/ +import "C" +import ( + "strings" + "sync" + "unsafe" +) + +const NSAlertStyleWarning = C.int(0) +const NSAlertStyleInformational = C.int(1) +const NSAlertStyleCritical = C.int(2) + +var alertTypeMap = map[DialogType]C.int{ + WarningDialogType: NSAlertStyleWarning, + InfoDialogType: NSAlertStyleInformational, + ErrorDialogType: NSAlertStyleCritical, + QuestionDialogType: NSAlertStyleInformational, +} + +type dialogResultCallback func(int) + +var ( + callbacks = make(map[int]dialogResultCallback) + mutex = &sync.Mutex{} +) + +func addDialogCallback(callback dialogResultCallback) int { + mutex.Lock() + defer mutex.Unlock() + + // Find the first free integer key + var id int + for { + if _, exists := callbacks[id]; !exists { + break + } + id++ + } + + // Save the function in the map using the integer key + callbacks[id] = callback + + // Return the key + return id +} + +func removeDialogCallback(id int) { + mutex.Lock() + defer mutex.Unlock() + delete(callbacks, id) +} + +//export dialogCallback +func dialogCallback(id C.int, buttonPressed C.int) { + mutex.Lock() + callback, exists := callbacks[int(id)] + mutex.Unlock() + + if !exists { + return + } + + // Call the function with the button number + callback(int(buttonPressed)) // Replace nil with the actual slice of buttons +} + +func (m *macosApp) showAboutDialog(title string, message string, icon []byte) { + var iconData unsafe.Pointer + if icon != nil { + iconData = unsafe.Pointer(&icon[0]) + } + InvokeAsync(func() { + C.showAboutBox(C.CString(title), C.CString(message), iconData, C.int(len(icon))) + }) +} + +type macosDialog struct { + dialog *MessageDialog + + nsDialog unsafe.Pointer +} + +func (m *macosDialog) show() { + InvokeAsync(func() { + + // Mac can only have 4 Buttons on a dialog + if len(m.dialog.Buttons) > 4 { + m.dialog.Buttons = m.dialog.Buttons[:4] + } + + if m.nsDialog != nil { + C.releaseDialog(m.nsDialog) + } + var title *C.char + if m.dialog.Title != "" { + title = C.CString(m.dialog.Title) + } + var message *C.char + if m.dialog.Message != "" { + message = C.CString(m.dialog.Message) + } + var iconData unsafe.Pointer + var iconLength C.int + if m.dialog.Icon != nil { + iconData = unsafe.Pointer(&m.dialog.Icon[0]) + iconLength = C.int(len(m.dialog.Icon)) + } else { + // if it's an error, use the application Icon + if m.dialog.DialogType == ErrorDialogType { + if globalApplication.options.Icon != nil { + iconData = unsafe.Pointer(&globalApplication.options.Icon[0]) + iconLength = C.int(len(globalApplication.options.Icon)) + } + } + } + var parent unsafe.Pointer + if m.dialog.window != nil { + // get NSWindow from window + parent = m.dialog.window.NativeWindow() + } + + alertType, ok := alertTypeMap[m.dialog.DialogType] + if !ok { + alertType = C.NSAlertStyleInformational + } + + m.nsDialog = C.createAlert(alertType, title, message, iconData, iconLength) + + // Reverse the Buttons so that the default is on the right + reversedButtons := make([]*Button, len(m.dialog.Buttons)) + var count = 0 + for i := len(m.dialog.Buttons) - 1; i >= 0; i-- { + button := m.dialog.Buttons[i] + C.alertAddButton(m.nsDialog, C.CString(button.Label), C.bool(button.IsDefault), C.bool(button.IsCancel)) + reversedButtons[count] = m.dialog.Buttons[i] + count++ + } + + var callBackID int + callBackID = addDialogCallback(func(buttonPressed int) { + if len(m.dialog.Buttons) > buttonPressed { + button := reversedButtons[buttonPressed] + if button.Callback != nil { + button.Callback() + } + } + removeDialogCallback(callBackID) + }) + + C.dialogRunModal(m.nsDialog, parent, C.int(callBackID)) + + }) + +} + +func newDialogImpl(d *MessageDialog) *macosDialog { + return &macosDialog{ + dialog: d, + } +} + +type macosOpenFileDialog struct { + dialog *OpenFileDialogStruct +} + +func newOpenFileDialogImpl(d *OpenFileDialogStruct) *macosOpenFileDialog { + return &macosOpenFileDialog{ + dialog: d, + } +} + +func toCString(s string) *C.char { + if s == "" { + return nil + } + return C.CString(s) +} + +func (m *macosOpenFileDialog) show() (chan string, error) { + openFileResponses[m.dialog.id] = make(chan string) + nsWindow := unsafe.Pointer(nil) + if m.dialog.window != nil { + // get NSWindow from window + nsWindow = m.dialog.window.NativeWindow() + } + + // Massage filter patterns into macOS format + // We iterate all filter patterns, tidy them up and then join them with a semicolon + // This should produce a single string of extensions like "png;jpg;gif" + var filterPatterns string + if len(m.dialog.filters) > 0 { + var allPatterns []string + for _, filter := range m.dialog.filters { + patternComponents := strings.Split(filter.Pattern, ";") + for i, component := range patternComponents { + filterPattern := strings.TrimSpace(component) + filterPattern = strings.TrimPrefix(filterPattern, "*.") + patternComponents[i] = filterPattern + } + allPatterns = append(allPatterns, strings.Join(patternComponents, ";")) + } + filterPatterns = strings.Join(allPatterns, ";") + } + C.showOpenFileDialog(C.uint(m.dialog.id), + C.bool(m.dialog.canChooseFiles), + C.bool(m.dialog.canChooseDirectories), + C.bool(m.dialog.canCreateDirectories), + C.bool(m.dialog.showHiddenFiles), + C.bool(m.dialog.allowsMultipleSelection), + C.bool(m.dialog.resolvesAliases), + C.bool(m.dialog.hideExtension), + C.bool(m.dialog.treatsFilePackagesAsDirectories), + C.bool(m.dialog.allowsOtherFileTypes), + toCString(filterPatterns), + C.uint(len(filterPatterns)), + toCString(m.dialog.message), + toCString(m.dialog.directory), + toCString(m.dialog.buttonText), + nsWindow) + + return openFileResponses[m.dialog.id], nil +} + +//export openFileDialogCallback +func openFileDialogCallback(cid C.uint, cpath *C.char) { + path := C.GoString(cpath) + id := uint(cid) + channel, ok := openFileResponses[id] + if ok { + channel <- path + } else { + panic("No channel found for open file dialog") + } +} + +//export openFileDialogCallbackEnd +func openFileDialogCallbackEnd(cid C.uint) { + id := uint(cid) + channel, ok := openFileResponses[id] + if ok { + close(channel) + delete(openFileResponses, id) + freeDialogID(id) + } else { + panic("No channel found for open file dialog") + } +} + +type macosSaveFileDialog struct { + dialog *SaveFileDialogStruct +} + +func newSaveFileDialogImpl(d *SaveFileDialogStruct) *macosSaveFileDialog { + return &macosSaveFileDialog{ + dialog: d, + } +} + +func (m *macosSaveFileDialog) show() (chan string, error) { + saveFileResponses[m.dialog.id] = make(chan string) + nsWindow := unsafe.Pointer(nil) + if m.dialog.window != nil { + // get NSWindow from window + nsWindow = m.dialog.window.NativeWindow() + } + C.showSaveFileDialog(C.uint(m.dialog.id), + C.bool(m.dialog.canCreateDirectories), + C.bool(m.dialog.showHiddenFiles), + C.bool(m.dialog.canSelectHiddenExtension), + C.bool(m.dialog.hideExtension), + C.bool(m.dialog.treatsFilePackagesAsDirectories), + C.bool(m.dialog.allowOtherFileTypes), + toCString(m.dialog.message), + toCString(m.dialog.directory), + toCString(m.dialog.buttonText), + toCString(m.dialog.filename), + nsWindow) + return saveFileResponses[m.dialog.id], nil +} + +//export saveFileDialogCallback +func saveFileDialogCallback(cid C.uint, cpath *C.char) { + // Covert the path to a string + path := C.GoString(cpath) + id := uint(cid) + // put response on channel + channel, ok := saveFileResponses[id] + if ok { + channel <- path + close(channel) + delete(saveFileResponses, id) + freeDialogID(id) + + } else { + panic("No channel found for save file dialog") + } +} diff --git a/v3/pkg/application/dialogs_darwin_delegate.h b/v3/pkg/application/dialogs_darwin_delegate.h new file mode 100644 index 000000000..d1c732a91 --- /dev/null +++ b/v3/pkg/application/dialogs_darwin_delegate.h @@ -0,0 +1,18 @@ +//go:build darwin + +#ifndef _DIALOGS_DELEGATE_H_ +#define _DIALOGS_DELEGATE_H_ + +#import + +// Conditionally import UniformTypeIdentifiers based on OS version +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 +#import +#endif + +// OpenPanel delegate to handle file filtering +@interface OpenPanelDelegate : NSObject +@property (nonatomic, strong) NSArray *allowedExtensions; +@end + +#endif \ No newline at end of file diff --git a/v3/pkg/application/dialogs_darwin_delegate.m b/v3/pkg/application/dialogs_darwin_delegate.m new file mode 100644 index 000000000..284f98ab8 --- /dev/null +++ b/v3/pkg/application/dialogs_darwin_delegate.m @@ -0,0 +1,38 @@ +//go:build darwin + +#import "dialogs_darwin_delegate.h" + +// Override shouldEnableURL +@implementation OpenPanelDelegate +- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url { + if (url == nil) { + return NO; + } + + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL isDirectory = NO; + if ([fileManager fileExistsAtPath:url.path isDirectory:&isDirectory] && isDirectory) { + return YES; + } + + // If no extensions specified, allow all files + if (self.allowedExtensions == nil || [self.allowedExtensions count] == 0) { + return YES; + } + + NSString *extension = [url.pathExtension lowercaseString]; + if (extension == nil || [extension isEqualToString:@""]) { + return NO; + } + + // Check if the extension is in our allowed list (case insensitive) + for (NSString *allowedExt in self.allowedExtensions) { + if ([[allowedExt lowercaseString] isEqualToString:extension]) { + return YES; + } + } + + return NO; +} + +@end diff --git a/v3/pkg/application/dialogs_linux.go b/v3/pkg/application/dialogs_linux.go new file mode 100644 index 000000000..018547ce3 --- /dev/null +++ b/v3/pkg/application/dialogs_linux.go @@ -0,0 +1,85 @@ +package application + +func (a *linuxApp) showAboutDialog(title string, message string, icon []byte) { + window, _ := globalApplication.Window.GetByID(a.getCurrentWindowID()) + var parent uintptr + if window != nil { + nativeWindow := window.NativeWindow() + if nativeWindow != nil { + parent = uintptr(nativeWindow) + } + } + about := newMessageDialog(InfoDialogType) + about.SetTitle(title). + SetMessage(message). + SetIcon(icon) + InvokeAsync(func() { + runQuestionDialog( + pointer(parent), + about, + ) + }) +} + +type linuxDialog struct { + dialog *MessageDialog +} + +func (m *linuxDialog) show() { + windowId := getNativeApplication().getCurrentWindowID() + window, _ := globalApplication.Window.GetByID(windowId) + var parent uintptr + if window != nil { + nativeWindow := window.NativeWindow() + if nativeWindow != nil { + parent = uintptr(nativeWindow) + } + } + + InvokeAsync(func() { + response := runQuestionDialog(pointer(parent), m.dialog) + if response >= 0 && response < len(m.dialog.Buttons) { + button := m.dialog.Buttons[response] + if button.Callback != nil { + go func() { + defer handlePanic() + button.Callback() + }() + } + } + }) +} + +func newDialogImpl(d *MessageDialog) *linuxDialog { + return &linuxDialog{ + dialog: d, + } +} + +type linuxOpenFileDialog struct { + dialog *OpenFileDialogStruct +} + +func newOpenFileDialogImpl(d *OpenFileDialogStruct) *linuxOpenFileDialog { + return &linuxOpenFileDialog{ + dialog: d, + } +} + +func (m *linuxOpenFileDialog) show() (chan string, error) { + return runOpenFileDialog(m.dialog) +} + +type linuxSaveFileDialog struct { + dialog *SaveFileDialogStruct +} + +func newSaveFileDialogImpl(d *SaveFileDialogStruct) *linuxSaveFileDialog { + return &linuxSaveFileDialog{ + dialog: d, + } +} + +func (m *linuxSaveFileDialog) show() (chan string, error) { + return runSaveFileDialog(m.dialog) +} diff --git a/v3/pkg/application/dialogs_windows.go b/v3/pkg/application/dialogs_windows.go new file mode 100644 index 000000000..73084d098 --- /dev/null +++ b/v3/pkg/application/dialogs_windows.go @@ -0,0 +1,275 @@ +//go:build windows + +package application + +import ( + "path/filepath" + "strings" + + "github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd" + "github.com/wailsapp/wails/v3/pkg/w32" + "golang.org/x/sys/windows" +) + +func (m *windowsApp) showAboutDialog(title string, message string, _ []byte) { + about := newDialogImpl(&MessageDialog{ + MessageDialogOptions: MessageDialogOptions{ + DialogType: InfoDialogType, + Title: title, + Message: message, + }, + }) + about.UseAppIcon = true + about.show() +} + +type windowsDialog struct { + dialog *MessageDialog + + //dialogImpl unsafe.Pointer + UseAppIcon bool +} + +func (m *windowsDialog) show() { + + title := w32.MustStringToUTF16Ptr(m.dialog.Title) + message := w32.MustStringToUTF16Ptr(m.dialog.Message) + flags := calculateMessageDialogFlags(m.dialog.MessageDialogOptions) + var button int32 + var err error + + var parentWindow uintptr + if m.dialog.window != nil { + nativeWindow := m.dialog.window.NativeWindow() + if nativeWindow != nil { + parentWindow = uintptr(nativeWindow) + } + } + + if m.UseAppIcon || m.dialog.Icon != nil { + // 3 is the application icon + button, err = w32.MessageBoxWithIcon(parentWindow, message, title, 3, windows.MB_OK|windows.MB_USERICON) + if err != nil { + globalApplication.handleFatalError(err) + } + } else { + button, err = windows.MessageBox(windows.HWND(parentWindow), message, title, flags|windows.MB_SYSTEMMODAL) + if err != nil { + globalApplication.handleFatalError(err) + } + } + // This maps MessageBox return values to strings + responses := []string{"", "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "", "", "Try Again", "Continue"} + result := "Error" + if int(button) < len(responses) { + result = responses[button] + } + // Check if there's a callback for the button pressed + for _, buttonInDialog := range m.dialog.Buttons { + if buttonInDialog.Label == result { + if buttonInDialog.Callback != nil { + buttonInDialog.Callback() + } + } + } +} + +func newDialogImpl(d *MessageDialog) *windowsDialog { + return &windowsDialog{ + dialog: d, + } +} + +type windowOpenFileDialog struct { + dialog *OpenFileDialogStruct +} + +func newOpenFileDialogImpl(d *OpenFileDialogStruct) *windowOpenFileDialog { + return &windowOpenFileDialog{ + dialog: d, + } +} + +func getDefaultFolder(folder string) (string, error) { + if folder == "" { + return "", nil + } + return filepath.Abs(folder) +} + +func (m *windowOpenFileDialog) show() (chan string, error) { + + defaultFolder, err := getDefaultFolder(m.dialog.directory) + if err != nil { + return nil, err + } + + config := cfd.DialogConfig{ + Title: m.dialog.title, + Role: "PickFolder", + FileFilters: convertFilters(m.dialog.filters), + Folder: defaultFolder, + } + + var result []string + if m.dialog.allowsMultipleSelection && !m.dialog.canChooseDirectories { + temp, err := showCfdDialog( + func() (cfd.Dialog, error) { + return cfd.NewOpenMultipleFilesDialog(config) + }, true, m.dialog.window) + if err != nil { + return nil, err + } + result = temp.([]string) + } else { + if m.dialog.canChooseDirectories { + temp, err := showCfdDialog( + func() (cfd.Dialog, error) { + return cfd.NewSelectFolderDialog(config) + }, false, m.dialog.window) + if err != nil { + return nil, err + } + result = []string{temp.(string)} + } else { + temp, err := showCfdDialog( + func() (cfd.Dialog, error) { + return cfd.NewOpenFileDialog(config) + }, false, m.dialog.window) + if err != nil { + return nil, err + } + result = []string{temp.(string)} + } + } + + files := make(chan string) + go func() { + defer handlePanic() + for _, file := range result { + files <- file + } + close(files) + }() + return files, nil +} + +type windowSaveFileDialog struct { + dialog *SaveFileDialogStruct +} + +func newSaveFileDialogImpl(d *SaveFileDialogStruct) *windowSaveFileDialog { + return &windowSaveFileDialog{ + dialog: d, + } +} + +func (m *windowSaveFileDialog) show() (chan string, error) { + files := make(chan string) + defaultFolder, err := getDefaultFolder(m.dialog.directory) + if err != nil { + close(files) + return files, err + } + + config := cfd.DialogConfig{ + Title: m.dialog.title, + Role: "SaveFile", + FileFilters: convertFilters(m.dialog.filters), + FileName: m.dialog.filename, + Folder: defaultFolder, + } + + // Original PR for v2 by @almas1992: https://github.com/wailsapp/wails/pull/3205 + if len(m.dialog.filters) > 0 { + config.DefaultExtension = strings.TrimPrefix(strings.Split(m.dialog.filters[0].Pattern, ";")[0], "*") + } + + result, err := showCfdDialog( + func() (cfd.Dialog, error) { + return cfd.NewSaveFileDialog(config) + }, false, m.dialog.window) + if err != nil { + close(files) + return files, err + } + go func() { + defer handlePanic() + f, ok := result.(string) + if ok { + files <- f + } + close(files) + }() + return files, err +} + +func calculateMessageDialogFlags(options MessageDialogOptions) uint32 { + var flags uint32 + + switch options.DialogType { + case InfoDialogType: + flags = windows.MB_OK | windows.MB_ICONINFORMATION + case ErrorDialogType: + flags = windows.MB_ICONERROR | windows.MB_OK + case QuestionDialogType: + flags = windows.MB_YESNO + for _, button := range options.Buttons { + if strings.TrimSpace(strings.ToLower(button.Label)) == "no" && button.IsDefault { + flags |= windows.MB_DEFBUTTON2 + } + } + case WarningDialogType: + flags = windows.MB_OK | windows.MB_ICONWARNING + } + + return flags +} + +func convertFilters(filters []FileFilter) []cfd.FileFilter { + var result []cfd.FileFilter + for _, filter := range filters { + result = append(result, cfd.FileFilter(filter)) + } + return result +} + +func showCfdDialog(newDlg func() (cfd.Dialog, error), isMultiSelect bool, parentWindow Window) (any, error) { + dlg, err := newDlg() + if err != nil { + return nil, err + } + + // Set parent window if provided + if parentWindow != nil { + nativeWindow := parentWindow.NativeWindow() + if nativeWindow != nil { + dlg.SetParentWindowHandle(uintptr(nativeWindow)) + } + } + + defer func() { + err := dlg.Release() + if err != nil { + globalApplication.error("unable to release dialog: %w", err) + } + }() + + if multi, _ := dlg.(cfd.OpenMultipleFilesDialog); multi != nil && isMultiSelect { + paths, err := multi.ShowAndGetResults() + if err != nil { + return nil, err + } + + for i, path := range paths { + paths[i] = filepath.Clean(path) + } + return paths, nil + } + + path, err := dlg.ShowAndGetResult() + if err != nil { + return nil, err + } + return filepath.Clean(path), nil +} diff --git a/v3/pkg/application/dialogs_windows_test.go b/v3/pkg/application/dialogs_windows_test.go new file mode 100644 index 000000000..af4dabf75 --- /dev/null +++ b/v3/pkg/application/dialogs_windows_test.go @@ -0,0 +1,57 @@ +//go:build windows + +package application_test + +import ( + "path/filepath" + "testing" + + "github.com/matryer/is" +) + +func TestCleanPath(t *testing.T) { + i := is.New(t) + tests := []struct { + name string + inputPath string + expected string + }{ + { + name: "path with double separators", + inputPath: `C:\\temp\\folder`, + expected: `C:\temp\folder`, + }, + { + name: "path with forward slashes", + inputPath: `C://temp//folder`, + expected: `C:\temp\folder`, + }, + { + name: "path with trailing separator", + inputPath: `C:\\temp\\folder\\`, + expected: `C:\temp\folder`, + }, + { + name: "path with escaped tab character", + inputPath: `C:\\Users\\test\\tab.txt`, + expected: `C:\Users\test\tab.txt`, + }, + { + name: "newline character", + inputPath: `C:\\Users\\test\\newline\\n.txt`, + expected: `C:\Users\test\newline\n.txt`, + }, + { + name: "UNC path with multiple separators", + inputPath: `\\\\\\\\host\\share\\test.txt`, + expected: `\\\\host\share\test.txt`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cleaned := filepath.Clean(tt.inputPath) + i.Equal(cleaned, tt.expected) + }) + } +} diff --git a/v3/pkg/application/environment.go b/v3/pkg/application/environment.go new file mode 100644 index 000000000..68be3ba06 --- /dev/null +++ b/v3/pkg/application/environment.go @@ -0,0 +1,18 @@ +package application + +import "github.com/wailsapp/wails/v3/internal/operatingsystem" + +// EnvironmentInfo represents information about the current environment. +// +// Fields: +// - OS: the operating system that the program is running on. +// - Arch: the architecture of the operating system. +// - Debug: indicates whether debug mode is enabled. +// - OSInfo: information about the operating system. +type EnvironmentInfo struct { + OS string + Arch string + Debug bool + OSInfo *operatingsystem.OS + PlatformInfo map[string]any +} diff --git a/v3/pkg/application/environment_manager.go b/v3/pkg/application/environment_manager.go new file mode 100644 index 000000000..d32b6dd89 --- /dev/null +++ b/v3/pkg/application/environment_manager.go @@ -0,0 +1,56 @@ +package application + +import ( + "runtime" + + "github.com/wailsapp/wails/v3/internal/fileexplorer" + "github.com/wailsapp/wails/v3/internal/operatingsystem" +) + +// EnvironmentManager manages environment-related operations +type EnvironmentManager struct { + app *App +} + +// newEnvironmentManager creates a new EnvironmentManager instance +func newEnvironmentManager(app *App) *EnvironmentManager { + return &EnvironmentManager{ + app: app, + } +} + +// Info returns environment information +func (em *EnvironmentManager) Info() EnvironmentInfo { + info, _ := operatingsystem.Info() + result := EnvironmentInfo{ + OS: runtime.GOOS, + Arch: runtime.GOARCH, + Debug: em.app.isDebugMode, + OSInfo: info, + } + result.PlatformInfo = em.app.platformEnvironment() + return result +} + +// IsDarkMode returns true if the system is in dark mode +func (em *EnvironmentManager) IsDarkMode() bool { + if em.app.impl == nil { + return false + } + return em.app.impl.isDarkMode() +} + +// GetAccentColor returns the system accent color +func (em *EnvironmentManager) GetAccentColor() string { + if em.app.impl == nil { + return "rgb(0,122,255)" + } + return em.app.impl.getAccentColor() +} + +// OpenFileManager opens the file manager at the specified path, optionally selecting the file +func (em *EnvironmentManager) OpenFileManager(path string, selectFile bool) error { + return InvokeSyncWithError(func() error { + return fileexplorer.OpenFileManager(path, selectFile) + }) +} diff --git a/v3/pkg/application/errors.go b/v3/pkg/application/errors.go new file mode 100644 index 000000000..d0b7edd6b --- /dev/null +++ b/v3/pkg/application/errors.go @@ -0,0 +1,55 @@ +package application + +import ( + "fmt" + "os" + "strings" +) + +// FatalError instances are passed to the registered error handler +// in case of catastrophic, unrecoverable failures that require immediate termination. +// FatalError wraps the original error value in an informative message. +// The underlying error may be retrieved through the [FatalError.Unwrap] method. +type FatalError struct { + err error + internal bool +} + +// Internal returns true when the error was triggered from wails' internal code. +func (e *FatalError) Internal() bool { + return e.internal +} + +// Unwrap returns the original cause of the fatal error, +// for easy inspection using the [errors.As] API. +func (e *FatalError) Unwrap() error { + return e.err +} + +func (e *FatalError) Error() string { + var buffer strings.Builder + buffer.WriteString("\n\n******************************** FATAL *********************************\n") + buffer.WriteString("* There has been a catastrophic failure in your application. *\n") + if e.internal { + buffer.WriteString("* Please report this error at https://github.com/wailsapp/wails/issues *\n") + } + buffer.WriteString("**************************** Error Details *****************************\n") + buffer.WriteString(e.err.Error()) + buffer.WriteString("************************************************************************\n") + return buffer.String() +} + +func Fatal(message string, args ...any) { + err := &FatalError{ + err: fmt.Errorf(message, args...), + internal: true, + } + + if globalApplication != nil { + globalApplication.handleError(err) + } else { + fmt.Println(err) + } + + os.Exit(1) +} diff --git a/v3/pkg/application/event_manager.go b/v3/pkg/application/event_manager.go new file mode 100644 index 000000000..1ee191e10 --- /dev/null +++ b/v3/pkg/application/event_manager.go @@ -0,0 +1,155 @@ +package application + +import ( + "slices" + + "github.com/samber/lo" + "github.com/wailsapp/wails/v3/pkg/events" +) + +// EventManager manages event-related operations +type EventManager struct { + app *App +} + +// newEventManager creates a new EventManager instance +func newEventManager(app *App) *EventManager { + return &EventManager{ + app: app, + } +} + +// Emit emits a custom event +func (em *EventManager) Emit(name string, data ...any) { + em.app.customEventProcessor.Emit(&CustomEvent{ + Name: name, + Data: data, + }) +} + +// EmitEvent emits a custom event object (internal use) +func (em *EventManager) EmitEvent(event *CustomEvent) { + em.app.customEventProcessor.Emit(event) +} + +// On registers a listener for custom events +func (em *EventManager) On(name string, callback func(event *CustomEvent)) func() { + return em.app.customEventProcessor.On(name, callback) +} + +// Off removes all listeners for a custom event +func (em *EventManager) Off(name string) { + em.app.customEventProcessor.Off(name) +} + +// OnMultiple registers a listener for custom events that will be called N times +func (em *EventManager) OnMultiple(name string, callback func(event *CustomEvent), counter int) { + em.app.customEventProcessor.OnMultiple(name, callback, counter) +} + +// Reset removes all custom event listeners +func (em *EventManager) Reset() { + em.app.customEventProcessor.OffAll() +} + +// OnApplicationEvent registers a listener for application events +func (em *EventManager) OnApplicationEvent(eventType events.ApplicationEventType, callback func(event *ApplicationEvent)) func() { + eventID := uint(eventType) + em.app.applicationEventListenersLock.Lock() + defer em.app.applicationEventListenersLock.Unlock() + listener := &EventListener{ + callback: callback, + } + em.app.applicationEventListeners[eventID] = append(em.app.applicationEventListeners[eventID], listener) + if em.app.impl != nil { + go func() { + defer handlePanic() + em.app.impl.on(eventID) + }() + } + + return func() { + // lock the map + em.app.applicationEventListenersLock.Lock() + defer em.app.applicationEventListenersLock.Unlock() + // Remove listener + em.app.applicationEventListeners[eventID] = lo.Without(em.app.applicationEventListeners[eventID], listener) + } +} + +// RegisterApplicationEventHook registers an application event hook +func (em *EventManager) RegisterApplicationEventHook(eventType events.ApplicationEventType, callback func(event *ApplicationEvent)) func() { + eventID := uint(eventType) + em.app.applicationEventHooksLock.Lock() + defer em.app.applicationEventHooksLock.Unlock() + thisHook := &eventHook{ + callback: callback, + } + em.app.applicationEventHooks[eventID] = append(em.app.applicationEventHooks[eventID], thisHook) + + return func() { + em.app.applicationEventHooksLock.Lock() + em.app.applicationEventHooks[eventID] = lo.Without(em.app.applicationEventHooks[eventID], thisHook) + em.app.applicationEventHooksLock.Unlock() + } +} + +// Dispatch dispatches an event to listeners (internal use) +func (em *EventManager) dispatch(event *CustomEvent) { + // Snapshot windows under RLock + em.app.windowsLock.RLock() + for _, window := range em.app.windows { + if event.IsCancelled() { + em.app.windowsLock.RUnlock() + return + } + window.DispatchWailsEvent(event) + } + em.app.windowsLock.RUnlock() + + // Snapshot listeners under Lock + em.app.wailsEventListenerLock.Lock() + listeners := slices.Clone(em.app.wailsEventListeners) + em.app.wailsEventListenerLock.Unlock() + + for _, listener := range listeners { + if event.IsCancelled() { + return + } + listener.DispatchWailsEvent(event) + } +} + +// HandleApplicationEvent handles application events (internal use) +func (em *EventManager) handleApplicationEvent(event *ApplicationEvent) { + defer handlePanic() + em.app.applicationEventListenersLock.RLock() + listeners, ok := em.app.applicationEventListeners[event.Id] + em.app.applicationEventListenersLock.RUnlock() + if !ok { + return + } + + // Process Hooks + em.app.applicationEventHooksLock.RLock() + hooks, ok := em.app.applicationEventHooks[event.Id] + em.app.applicationEventHooksLock.RUnlock() + if ok { + for _, thisHook := range hooks { + thisHook.callback(event) + if event.IsCancelled() { + return + } + } + } + + for _, listener := range listeners { + go func() { + if event.IsCancelled() { + return + } + defer handlePanic() + listener.callback(event) + }() + } +} diff --git a/v3/pkg/application/events.go b/v3/pkg/application/events.go new file mode 100644 index 000000000..973c3015c --- /dev/null +++ b/v3/pkg/application/events.go @@ -0,0 +1,266 @@ +package application + +import ( + "encoding/json" + "sync" + + "github.com/samber/lo" + "github.com/wailsapp/wails/v3/pkg/events" +) + +type ApplicationEvent struct { + Id uint + ctx *ApplicationEventContext + cancelled bool + lock sync.RWMutex +} + +func (w *ApplicationEvent) Context() *ApplicationEventContext { + return w.ctx +} + +func newApplicationEvent(id events.ApplicationEventType) *ApplicationEvent { + return &ApplicationEvent{ + Id: uint(id), + ctx: newApplicationEventContext(), + } +} + +func (w *ApplicationEvent) Cancel() { + w.lock.Lock() + defer w.lock.Unlock() + w.cancelled = true +} + +func (w *ApplicationEvent) IsCancelled() bool { + w.lock.RLock() + defer w.lock.RUnlock() + return w.cancelled +} + +var applicationEvents = make(chan *ApplicationEvent, 5) + +type windowEvent struct { + WindowID uint + EventID uint +} + +var windowEvents = make(chan *windowEvent, 5) + +var menuItemClicked = make(chan uint, 5) + +type CustomEvent struct { + Name string `json:"name"` + Data any `json:"data"` + Sender string `json:"sender"` // Name of the window sending the event, or "" if sent from application + cancelled bool + lock sync.RWMutex +} + +func (e *CustomEvent) Cancel() { + e.lock.Lock() + defer e.lock.Unlock() + e.cancelled = true +} + +func (e *CustomEvent) IsCancelled() bool { + e.lock.Lock() + defer e.lock.Unlock() + return e.cancelled +} + +func (e *CustomEvent) ToJSON() string { + marshal, err := json.Marshal(&e) + if err != nil { + // TODO: Fatal error? log? + return "" + } + return string(marshal) +} + +// WailsEventListener is an interface that can be implemented to listen for Wails events +// It is used by the RegisterListener method of the Application. +type WailsEventListener interface { + DispatchWailsEvent(event *CustomEvent) +} + +type hook struct { + callback func(*CustomEvent) +} + +// eventListener holds a callback function which is invoked when +// the event listened for is emitted. It has a counter which indicates +// how the total number of events it is interested in. A value of zero +// means it does not expire (default). +type eventListener struct { + callback func(*CustomEvent) // Function to call with emitted event data + counter int // The number of times this callback may be called. -1 = infinite + delete bool // Flag to indicate that this listener should be deleted +} + +// EventProcessor handles custom events +type EventProcessor struct { + // Go event listeners + listeners map[string][]*eventListener + notifyLock sync.RWMutex + dispatchEventToWindows func(*CustomEvent) + hooks map[string][]*hook + hookLock sync.RWMutex +} + +func NewWailsEventProcessor(dispatchEventToWindows func(*CustomEvent)) *EventProcessor { + return &EventProcessor{ + listeners: make(map[string][]*eventListener), + dispatchEventToWindows: dispatchEventToWindows, + hooks: make(map[string][]*hook), + } +} + +// On is the equivalent of Javascript's `addEventListener` +func (e *EventProcessor) On(eventName string, callback func(event *CustomEvent)) func() { + return e.registerListener(eventName, callback, -1) +} + +// OnMultiple is the same as `OnApplicationEvent` but will unregister after `count` events +func (e *EventProcessor) OnMultiple(eventName string, callback func(event *CustomEvent), counter int) func() { + return e.registerListener(eventName, callback, counter) +} + +// Once is the same as `OnApplicationEvent` but will unregister after the first event +func (e *EventProcessor) Once(eventName string, callback func(event *CustomEvent)) func() { + return e.registerListener(eventName, callback, 1) +} + +// Emit sends an event to all listeners +func (e *EventProcessor) Emit(thisEvent *CustomEvent) { + if thisEvent == nil { + return + } + + // If we have any hooks, run them first and check if the event was cancelled + if e.hooks != nil { + if hooks, ok := e.hooks[thisEvent.Name]; ok { + for _, thisHook := range hooks { + thisHook.callback(thisEvent) + if thisEvent.IsCancelled() { + return + } + } + } + } + + go func() { + defer handlePanic() + e.dispatchEventToListeners(thisEvent) + }() + go func() { + defer handlePanic() + e.dispatchEventToWindows(thisEvent) + }() +} + +func (e *EventProcessor) Off(eventName string) { + e.unRegisterListener(eventName) +} + +func (e *EventProcessor) OffAll() { + e.notifyLock.Lock() + defer e.notifyLock.Unlock() + e.listeners = make(map[string][]*eventListener) +} + +// registerListener provides a means of subscribing to events of type "eventName" +func (e *EventProcessor) registerListener(eventName string, callback func(*CustomEvent), counter int) func() { + // Create new eventListener + thisListener := &eventListener{ + callback: callback, + counter: counter, + delete: false, + } + e.notifyLock.Lock() + // Append the new listener to the listeners slice + e.listeners[eventName] = append(e.listeners[eventName], thisListener) + e.notifyLock.Unlock() + return func() { + e.notifyLock.Lock() + defer e.notifyLock.Unlock() + + if _, ok := e.listeners[eventName]; !ok { + return + } + e.listeners[eventName] = lo.Filter(e.listeners[eventName], func(l *eventListener, i int) bool { + return l != thisListener + }) + } +} + +// RegisterHook provides a means of registering methods to be called before emitting the event +func (e *EventProcessor) RegisterHook(eventName string, callback func(*CustomEvent)) func() { + // Create new hook + thisHook := &hook{ + callback: callback, + } + e.hookLock.Lock() + // Append the new listener to the listeners slice + e.hooks[eventName] = append(e.hooks[eventName], thisHook) + e.hookLock.Unlock() + return func() { + e.hookLock.Lock() + defer e.hookLock.Unlock() + + if _, ok := e.hooks[eventName]; !ok { + return + } + e.hooks[eventName] = lo.Filter(e.hooks[eventName], func(l *hook, i int) bool { + return l != thisHook + }) + } +} + +// unRegisterListener provides a means of unsubscribing to events of type "eventName" +func (e *EventProcessor) unRegisterListener(eventName string) { + e.notifyLock.Lock() + defer e.notifyLock.Unlock() + delete(e.listeners, eventName) +} + +// dispatchEventToListeners calls all registered listeners event name +func (e *EventProcessor) dispatchEventToListeners(event *CustomEvent) { + + e.notifyLock.Lock() + defer e.notifyLock.Unlock() + + listeners := e.listeners[event.Name] + if listeners == nil { + return + } + + // We have a dirty flag to indicate that there are items to delete + itemsToDelete := false + + // Callback in goroutine + for _, listener := range listeners { + if listener.counter > 0 { + listener.counter-- + } + go func() { + if event.IsCancelled() { + return + } + defer handlePanic() + listener.callback(event) + }() + + if listener.counter == 0 { + listener.delete = true + itemsToDelete = true + } + } + + // Do we have items to delete? + if itemsToDelete == true { + e.listeners[event.Name] = lo.Filter(listeners, func(l *eventListener, i int) bool { + return l.delete == false + }) + } +} diff --git a/v3/pkg/application/events_common_darwin.go b/v3/pkg/application/events_common_darwin.go new file mode 100644 index 000000000..6fce7fcd4 --- /dev/null +++ b/v3/pkg/application/events_common_darwin.go @@ -0,0 +1,21 @@ +//go:build darwin + +package application + +import "github.com/wailsapp/wails/v3/pkg/events" + +var commonApplicationEventMap = map[events.ApplicationEventType]events.ApplicationEventType{ + events.Mac.ApplicationDidFinishLaunching: events.Common.ApplicationStarted, + events.Mac.ApplicationDidChangeTheme: events.Common.ThemeChanged, +} + +func (m *macosApp) setupCommonEvents() { + for sourceEvent, targetEvent := range commonApplicationEventMap { + sourceEvent := sourceEvent + targetEvent := targetEvent + m.parent.Event.OnApplicationEvent(sourceEvent, func(event *ApplicationEvent) { + event.Id = uint(targetEvent) + applicationEvents <- event + }) + } +} diff --git a/v3/pkg/application/events_common_linux.go b/v3/pkg/application/events_common_linux.go new file mode 100644 index 000000000..d16232648 --- /dev/null +++ b/v3/pkg/application/events_common_linux.go @@ -0,0 +1,21 @@ +//go:build linux + +package application + +import "github.com/wailsapp/wails/v3/pkg/events" + +var commonApplicationEventMap = map[events.ApplicationEventType]events.ApplicationEventType{ + events.Linux.ApplicationStartup: events.Common.ApplicationStarted, + events.Linux.SystemThemeChanged: events.Common.ThemeChanged, +} + +func (a *linuxApp) setupCommonEvents() { + for sourceEvent, targetEvent := range commonApplicationEventMap { + sourceEvent := sourceEvent + targetEvent := targetEvent + a.parent.Event.OnApplicationEvent(sourceEvent, func(event *ApplicationEvent) { + event.Id = uint(targetEvent) + applicationEvents <- event + }) + } +} diff --git a/v3/pkg/application/events_common_windows.go b/v3/pkg/application/events_common_windows.go new file mode 100644 index 000000000..1c4dd55e6 --- /dev/null +++ b/v3/pkg/application/events_common_windows.go @@ -0,0 +1,21 @@ +//go:build windows + +package application + +import "github.com/wailsapp/wails/v3/pkg/events" + +var commonApplicationEventMap = map[events.ApplicationEventType]events.ApplicationEventType{ + events.Windows.SystemThemeChanged: events.Common.ThemeChanged, + events.Windows.ApplicationStarted: events.Common.ApplicationStarted, +} + +func (m *windowsApp) setupCommonEvents() { + for sourceEvent, targetEvent := range commonApplicationEventMap { + sourceEvent := sourceEvent + targetEvent := targetEvent + m.parent.Event.OnApplicationEvent(sourceEvent, func(event *ApplicationEvent) { + event.Id = uint(targetEvent) + applicationEvents <- event + }) + } +} diff --git a/v3/pkg/application/events_test.go b/v3/pkg/application/events_test.go new file mode 100644 index 000000000..ee1c26b2f --- /dev/null +++ b/v3/pkg/application/events_test.go @@ -0,0 +1,135 @@ +package application_test + +import ( + "sync" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + + "github.com/matryer/is" +) + +type mockNotifier struct { + Events []*application.CustomEvent +} + +func (m *mockNotifier) dispatchEventToWindows(event *application.CustomEvent) { + m.Events = append(m.Events, event) +} + +func (m *mockNotifier) Reset() { + m.Events = []*application.CustomEvent{} +} + +func Test_EventsOn(t *testing.T) { + i := is.New(t) + notifier := &mockNotifier{} + eventProcessor := application.NewWailsEventProcessor(notifier.dispatchEventToWindows) + + // Test OnApplicationEvent + eventName := "test" + counter := 0 + var wg sync.WaitGroup + wg.Add(1) + unregisterFn := eventProcessor.On(eventName, func(event *application.CustomEvent) { + // This is called in a goroutine + counter++ + wg.Done() + }) + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + wg.Wait() + i.Equal(1, counter) + + // Unregister + notifier.Reset() + unregisterFn() + counter = 0 + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + i.Equal(0, counter) + +} + +func Test_EventsOnce(t *testing.T) { + i := is.New(t) + notifier := &mockNotifier{} + eventProcessor := application.NewWailsEventProcessor(notifier.dispatchEventToWindows) + + // Test OnApplicationEvent + eventName := "test" + counter := 0 + var wg sync.WaitGroup + wg.Add(1) + unregisterFn := eventProcessor.Once(eventName, func(event *application.CustomEvent) { + // This is called in a goroutine + counter++ + wg.Done() + }) + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + wg.Wait() + i.Equal(1, counter) + + // Unregister + notifier.Reset() + unregisterFn() + counter = 0 + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + i.Equal(0, counter) + +} +func Test_EventsOnMultiple(t *testing.T) { + i := is.New(t) + notifier := &mockNotifier{} + eventProcessor := application.NewWailsEventProcessor(notifier.dispatchEventToWindows) + + // Test OnApplicationEvent + eventName := "test" + counter := 0 + var wg sync.WaitGroup + wg.Add(2) + unregisterFn := eventProcessor.OnMultiple(eventName, func(event *application.CustomEvent) { + // This is called in a goroutine + counter++ + wg.Done() + }, 2) + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + wg.Wait() + i.Equal(2, counter) + + // Unregister + notifier.Reset() + unregisterFn() + counter = 0 + eventProcessor.Emit(&application.CustomEvent{ + Name: "test", + Data: "test payload", + }) + i.Equal(0, counter) + +} diff --git a/v3/pkg/application/image.go b/v3/pkg/application/image.go new file mode 100644 index 000000000..81c519739 --- /dev/null +++ b/v3/pkg/application/image.go @@ -0,0 +1,37 @@ +package application + +import ( + "bytes" + "image" + "image/draw" + "image/png" +) + +func pngToImage(data []byte) (*image.RGBA, error) { + img, err := png.Decode(bytes.NewReader(data)) + if err != nil { + return nil, err + } + + bounds := img.Bounds() + rgba := image.NewRGBA(bounds) + draw.Draw(rgba, bounds, img, bounds.Min, draw.Src) + return rgba, nil +} + +func ToARGB(img *image.RGBA) (int, int, []byte) { + w, h := img.Bounds().Dx(), img.Bounds().Dy() + data := make([]byte, w*h*4) + i := 0 + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + r, g, b, a := img.At(x, y).RGBA() + data[i] = byte(a) + data[i+1] = byte(r) + data[i+2] = byte(g) + data[i+3] = byte(b) + i += 4 + } + } + return w, h, data +} diff --git a/v3/pkg/application/internal/tests/services/common.go b/v3/pkg/application/internal/tests/services/common.go new file mode 100644 index 000000000..9d35da69e --- /dev/null +++ b/v3/pkg/application/internal/tests/services/common.go @@ -0,0 +1,168 @@ +package services + +import ( + "context" + "fmt" + "sync/atomic" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Config struct { + Id int + T *testing.T + Seq *atomic.Int64 + Options application.ServiceOptions + StartupErr bool + ShutdownErr bool +} + +func Configure[T any, P interface { + *T + Configure(Config) +}](srv P, c Config) application.Service { + srv.Configure(c) + return application.NewServiceWithOptions(srv, c.Options) +} + +type Error struct { + Id int +} + +func (e *Error) Error() string { + return fmt.Sprintf("service #%d mock failure", e.Id) +} + +type Startupper struct { + Config + startup int64 +} + +func (s *Startupper) Configure(c Config) { + s.Config = c +} + +func (s *Startupper) Id() int { + return s.Config.Id +} + +func (s *Startupper) StartupSeq() int64 { + return s.startup +} + +func (s *Startupper) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + if s.startup != 0 { + s.T.Errorf("Double startup for service #%d: first at seq=%d, then at seq=%d", s.Id(), s.startup, s.Seq.Load()) + return nil + } + + s.startup = s.Seq.Add(1) + + if diff := cmp.Diff(s.Options, options); diff != "" { + s.T.Errorf("Options mismatch for service #%d (-want +got):\n%s", s.Id(), diff) + } + + if s.StartupErr { + return &Error{Id: s.Id()} + } else { + return nil + } +} + +type Shutdowner struct { + Config + shutdown int64 +} + +func (s *Shutdowner) Configure(c Config) { + s.Config = c +} + +func (s *Shutdowner) Id() int { + return s.Config.Id +} + +func (s *Shutdowner) ShutdownSeq() int64 { + return s.shutdown +} + +func (s *Shutdowner) ServiceShutdown() error { + if s.shutdown != 0 { + s.T.Errorf("Double shutdown for service #%d: first at seq=%d, then at seq=%d", s.Id(), s.shutdown, s.Seq.Load()) + return nil + } + + s.shutdown = s.Seq.Add(1) + + if s.ShutdownErr { + return &Error{Id: s.Id()} + } else { + return nil + } +} + +type StartupShutdowner struct { + Config + startup int64 + shutdown int64 + ctx context.Context +} + +func (s *StartupShutdowner) Configure(c Config) { + s.Config = c +} + +func (s *StartupShutdowner) Id() int { + return s.Config.Id +} + +func (s *StartupShutdowner) StartupSeq() int64 { + return s.startup +} + +func (s *StartupShutdowner) ShutdownSeq() int64 { + return s.shutdown +} + +func (s *StartupShutdowner) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + if s.startup != 0 { + s.T.Errorf("Double startup for service #%d: first at seq=%d, then at seq=%d", s.Id(), s.startup, s.Seq.Load()) + return nil + } + + s.startup = s.Seq.Add(1) + s.ctx = ctx + + if diff := cmp.Diff(s.Options, options); diff != "" { + s.T.Errorf("Options mismatch for service #%d (-want +got):\n%s", s.Id(), diff) + } + + if s.StartupErr { + return &Error{Id: s.Id()} + } else { + return nil + } +} + +func (s *StartupShutdowner) ServiceShutdown() error { + if s.shutdown != 0 { + s.T.Errorf("Double shutdown for service #%d: first at seq=%d, then at seq=%d", s.Id(), s.shutdown, s.Seq.Load()) + return nil + } + + s.shutdown = s.Seq.Add(1) + + select { + case <-s.ctx.Done(): + default: + s.T.Errorf("Service #%d shut down before context cancellation", s.Id()) + } + + if s.ShutdownErr { + return &Error{Id: s.Id()} + } else { + return nil + } +} diff --git a/v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go b/v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go new file mode 100644 index 000000000..ad8df141c --- /dev/null +++ b/v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go @@ -0,0 +1,92 @@ +package shutdown + +import ( + "sync" + "sync/atomic" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + apptest "github.com/wailsapp/wails/v3/pkg/application/internal/tests" + svctest "github.com/wailsapp/wails/v3/pkg/application/internal/tests/services" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func TestMain(m *testing.M) { + apptest.Main(m) +} + +type ( + Service1 struct{ svctest.Shutdowner } + Service2 struct{ svctest.Shutdowner } + Service3 struct{ svctest.Shutdowner } + Service4 struct{ svctest.Shutdowner } + Service5 struct{ svctest.Shutdowner } + Service6 struct{ svctest.Shutdowner } +) + +func TestServiceShutdown(t *testing.T) { + var seq atomic.Int64 + + services := []application.Service{ + svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}), + svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq}), + svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq}), + svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq}), + svctest.Configure(&Service5{}, svctest.Config{Id: 4, T: t, Seq: &seq}), + svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq}), + } + + app := apptest.New(t, application.Options{ + Services: services[:3], + }) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + app.RegisterService(services[3]) + wg.Done() + }() + go func() { + app.RegisterService(services[4]) + wg.Done() + }() + wg.Wait() + + app.RegisterService(services[5]) + + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) { + app.Quit() + }) + + err := apptest.Run(t, app) + if err != nil { + t.Fatal(err) + } + + if count := seq.Load(); count != int64(len(services)) { + t.Errorf("Wrong shutdown call count: wanted %d, got %d", len(services), count) + } + + validate(t, services[0], 5) + validate(t, services[1], 4) + validate(t, services[2], 2, 3) + validate(t, services[3], 1) + validate(t, services[4], 1) + validate(t, services[5], 0) +} + +func validate(t *testing.T, svc application.Service, prev ...int64) { + id := svc.Instance().(interface{ Id() int }).Id() + seq := svc.Instance().(interface{ ShutdownSeq() int64 }).ShutdownSeq() + + if seq == 0 { + t.Errorf("Service #%d did not shut down", id) + return + } + + for _, p := range prev { + if seq <= p { + t.Errorf("Wrong shutdown sequence number for service #%d: wanted >%d, got %d", id, p, seq) + } + } +} diff --git a/v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go b/v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go new file mode 100644 index 000000000..42949b037 --- /dev/null +++ b/v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go @@ -0,0 +1,123 @@ +package shutdownerror + +import ( + "errors" + "slices" + "sync" + "sync/atomic" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + apptest "github.com/wailsapp/wails/v3/pkg/application/internal/tests" + svctest "github.com/wailsapp/wails/v3/pkg/application/internal/tests/services" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func TestMain(m *testing.M) { + apptest.Main(m) +} + +type ( + Service1 struct{ svctest.Shutdowner } + Service2 struct{ svctest.Shutdowner } + Service3 struct{ svctest.Shutdowner } + Service4 struct{ svctest.Shutdowner } + Service5 struct{ svctest.Shutdowner } + Service6 struct{ svctest.Shutdowner } +) + +func TestServiceShutdownError(t *testing.T) { + var seq atomic.Int64 + + services := []application.Service{ + svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}), + svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq, ShutdownErr: true}), + svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq}), + svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq}), + svctest.Configure(&Service5{}, svctest.Config{Id: 4, T: t, Seq: &seq, ShutdownErr: true}), + svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq, ShutdownErr: true}), + } + + expectedShutdownErrors := []int{5, 4, 1} + var errCount atomic.Int64 + + var app *application.App + app = apptest.New(t, application.Options{ + Services: services[:3], + ErrorHandler: func(err error) { + var mock *svctest.Error + if !errors.As(err, &mock) { + app.Logger.Error(err.Error()) + return + } + + i := int(errCount.Add(1) - 1) + if i < len(expectedShutdownErrors) && mock.Id == expectedShutdownErrors[i] { + return + } + + cut := min(i, len(expectedShutdownErrors)) + if slices.Contains(expectedShutdownErrors[:cut], mock.Id) { + t.Errorf("Late or duplicate shutdown error for service #%d", mock.Id) + } else if slices.Contains(expectedShutdownErrors[cut:], mock.Id) { + t.Errorf("Early shutdown error for service #%d", mock.Id) + } else { + t.Errorf("Unexpected shutdown error for service #%d", mock.Id) + } + }, + }) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + app.RegisterService(services[3]) + wg.Done() + }() + go func() { + app.RegisterService(services[4]) + wg.Done() + }() + wg.Wait() + + app.RegisterService(services[5]) + + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) { + app.Quit() + }) + + err := apptest.Run(t, app) + if err != nil { + t.Fatal(err) + } + + if ec := errCount.Load(); ec != int64(len(expectedShutdownErrors)) { + t.Errorf("Wrong shutdown error count: wanted %d, got %d", len(expectedShutdownErrors), ec) + } + + if count := seq.Load(); count != int64(len(services)) { + t.Errorf("Wrong shutdown call count: wanted %d, got %d", len(services), count) + } + + validate(t, services[0], 5) + validate(t, services[1], 4) + validate(t, services[2], 2, 3) + validate(t, services[3], 1) + validate(t, services[4], 1) + validate(t, services[5], 0) +} + +func validate(t *testing.T, svc application.Service, prev ...int64) { + id := svc.Instance().(interface{ Id() int }).Id() + seq := svc.Instance().(interface{ ShutdownSeq() int64 }).ShutdownSeq() + + if seq == 0 { + t.Errorf("Service #%d did not shut down", id) + return + } + + for _, p := range prev { + if seq <= p { + t.Errorf("Wrong shutdown sequence number for service #%d: wanted >%d, got %d", id, p, seq) + } + } +} diff --git a/v3/pkg/application/internal/tests/services/startup/startup_test.go b/v3/pkg/application/internal/tests/services/startup/startup_test.go new file mode 100644 index 000000000..bf5c2ac96 --- /dev/null +++ b/v3/pkg/application/internal/tests/services/startup/startup_test.go @@ -0,0 +1,102 @@ +package startup + +import ( + "sync" + "sync/atomic" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + apptest "github.com/wailsapp/wails/v3/pkg/application/internal/tests" + svctest "github.com/wailsapp/wails/v3/pkg/application/internal/tests/services" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func TestMain(m *testing.M) { + apptest.Main(m) +} + +type ( + Service1 struct{ svctest.Startupper } + Service2 struct{ svctest.Startupper } + Service3 struct{ svctest.Startupper } + Service4 struct{ svctest.Startupper } + Service5 struct{ svctest.Startupper } + Service6 struct{ svctest.Startupper } +) + +func TestServiceStartup(t *testing.T) { + var seq atomic.Int64 + + services := []application.Service{ + svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}), + svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq, Options: application.ServiceOptions{ + Name: "I am service 2", + }}), + svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq, Options: application.ServiceOptions{ + Route: "/mounted/here", + }}), + svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq}), + svctest.Configure(&Service5{}, svctest.Config{Id: 4, Seq: &seq, Options: application.ServiceOptions{ + Name: "I am service 5", + Route: "/mounted/there", + }}), + svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq, Options: application.ServiceOptions{ + Name: "I am service 6", + Route: "/mounted/elsewhere", + }}), + } + + app := apptest.New(t, application.Options{ + Services: services[:3], + }) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + app.RegisterService(services[3]) + wg.Done() + }() + go func() { + app.RegisterService(services[4]) + wg.Done() + }() + wg.Wait() + + app.RegisterService(services[5]) + + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) { + app.Quit() + }) + + err := apptest.Run(t, app) + if err != nil { + t.Fatal(err) + } + + if count := seq.Load(); count != int64(len(services)) { + t.Errorf("Wrong startup call count: wanted %d, got %d", len(services), count) + } + + validate(t, services[0], 0) + validate(t, services[1], 1) + validate(t, services[2], 2) + validate(t, services[3], 3) + validate(t, services[4], 3) + validate(t, services[5], 4, 5) +} + +func validate(t *testing.T, svc application.Service, prev ...int64) { + id := svc.Instance().(interface{ Id() int }).Id() + seq := svc.Instance().(interface{ StartupSeq() int64 }).StartupSeq() + + if seq == 0 { + t.Errorf("Service #%d did not start up", id) + return + } + + for _, p := range prev { + if seq <= p { + t.Errorf("Wrong startup sequence number for service #%d: wanted >%d, got %d", id, p, seq) + } + } +} diff --git a/v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go b/v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go new file mode 100644 index 000000000..a4a3c5bc4 --- /dev/null +++ b/v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go @@ -0,0 +1,114 @@ +package startuperror + +import ( + "errors" + "sync" + "sync/atomic" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + apptest "github.com/wailsapp/wails/v3/pkg/application/internal/tests" + svctest "github.com/wailsapp/wails/v3/pkg/application/internal/tests/services" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func TestMain(m *testing.M) { + apptest.Main(m) +} + +type ( + Service1 struct{ svctest.Startupper } + Service2 struct{ svctest.Startupper } + Service3 struct{ svctest.Startupper } + Service4 struct{ svctest.Startupper } + Service5 struct{ svctest.Startupper } + Service6 struct{ svctest.Startupper } +) + +func TestServiceStartupError(t *testing.T) { + var seq atomic.Int64 + + services := []application.Service{ + svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}), + svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq}), + svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq}), + svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq, StartupErr: true}), + svctest.Configure(&Service5{}, svctest.Config{Id: 4, T: t, Seq: &seq, StartupErr: true}), + svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq, StartupErr: true}), + } + + app := apptest.New(t, application.Options{ + Services: services[:3], + }) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + app.RegisterService(services[3]) + wg.Done() + }() + go func() { + app.RegisterService(services[4]) + wg.Done() + }() + wg.Wait() + + app.RegisterService(services[5]) + + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) { + t.Errorf("Application started") + app.Quit() + }) + + var mock *svctest.Error + + err := apptest.Run(t, app) + if err != nil { + if !errors.As(err, &mock) { + t.Fatal(err) + } + } + + if mock == nil { + t.Fatal("Wanted startup error for service #3 or #4, got none") + } else if mock.Id != 3 && mock.Id != 4 { + t.Errorf("Wanted startup error for service #3 or #4, got #%d", mock.Id) + } + + if count := seq.Load(); count != 4 { + t.Errorf("Wrong startup call count: wanted %d, got %d", 4, count) + } + + validate(t, services[0], 0) + validate(t, services[1], 1) + validate(t, services[2], 2) + validate(t, services[mock.Id], 3) + + notStarted := 3 + if mock.Id == 3 { + notStarted = 4 + } + + if seq := services[notStarted].Instance().(interface{ StartupSeq() int64 }).StartupSeq(); seq != 0 { + t.Errorf("Service #%d started up unexpectedly at seq=%d", notStarted, seq) + } + if seq := services[5].Instance().(interface{ StartupSeq() int64 }).StartupSeq(); seq != 0 { + t.Errorf("Service #5 started up unexpectedly at seq=%d", seq) + } +} + +func validate(t *testing.T, svc application.Service, prev ...int64) { + id := svc.Instance().(interface{ Id() int }).Id() + seq := svc.Instance().(interface{ StartupSeq() int64 }).StartupSeq() + + if seq == 0 { + t.Errorf("Service #%d did not start up", id) + return + } + + for _, p := range prev { + if seq <= p { + t.Errorf("Wrong startup sequence number for service #%d: wanted >%d, got %d", id, p, seq) + } + } +} diff --git a/v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go b/v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go new file mode 100644 index 000000000..5f8cfc365 --- /dev/null +++ b/v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go @@ -0,0 +1,102 @@ +package startupshutdown + +import ( + "sync" + "sync/atomic" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + apptest "github.com/wailsapp/wails/v3/pkg/application/internal/tests" + svctest "github.com/wailsapp/wails/v3/pkg/application/internal/tests/services" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func TestMain(m *testing.M) { + apptest.Main(m) +} + +type ( + Service1 struct{ svctest.StartupShutdowner } + Service2 struct{ svctest.StartupShutdowner } + Service3 struct{ svctest.StartupShutdowner } + Service4 struct{ svctest.StartupShutdowner } + Service5 struct{ svctest.StartupShutdowner } + Service6 struct{ svctest.StartupShutdowner } +) + +func TestServiceStartupShutdown(t *testing.T) { + var seq atomic.Int64 + + services := []application.Service{ + svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}), + svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq}), + svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq}), + svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq}), + svctest.Configure(&Service5{}, svctest.Config{Id: 4, T: t, Seq: &seq}), + svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq}), + } + + app := apptest.New(t, application.Options{ + Services: services[:3], + }) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + app.RegisterService(services[3]) + wg.Done() + }() + go func() { + app.RegisterService(services[4]) + wg.Done() + }() + wg.Wait() + + app.RegisterService(services[5]) + + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) { + if count := seq.Load(); count != int64(len(services)) { + t.Errorf("Wrong startup call count: wanted %d, got %d", len(services), count) + } + seq.Store(0) + app.Quit() + }) + + err := apptest.Run(t, app) + if err != nil { + t.Fatal(err) + } + + if count := seq.Load(); count != int64(len(services)) { + t.Errorf("Wrong shutdown call count: wanted %d, got %d", len(services), count) + } + + bound := int64(len(services)) + 1 + validate(t, services[0], bound) + validate(t, services[1], bound) + validate(t, services[2], bound) + validate(t, services[3], bound) + validate(t, services[4], bound) + validate(t, services[5], bound) +} + +func validate(t *testing.T, svc application.Service, bound int64) { + id := svc.Instance().(interface{ Id() int }).Id() + startup := svc.Instance().(interface{ StartupSeq() int64 }).StartupSeq() + shutdown := svc.Instance().(interface{ ShutdownSeq() int64 }).ShutdownSeq() + + if startup == 0 && shutdown == 0 { + t.Errorf("Service #%d did not start nor shut down", id) + return + } else if startup == 0 { + t.Errorf("Service #%d started, but did not shut down", id) + return + } else if shutdown == 0 { + t.Errorf("Service #%d shut down, but did not start", id) + return + } + + if shutdown != bound-startup { + t.Errorf("Wrong sequence numbers for service #%d: wanted either %d..%d or %d..%d, got %d..%d", id, startup, bound-startup, bound-shutdown, shutdown, startup, shutdown) + } +} diff --git a/v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go b/v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go new file mode 100644 index 000000000..0ca135269 --- /dev/null +++ b/v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go @@ -0,0 +1,140 @@ +package startupshutdownerror + +import ( + "errors" + "slices" + "sync" + "sync/atomic" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" + apptest "github.com/wailsapp/wails/v3/pkg/application/internal/tests" + svctest "github.com/wailsapp/wails/v3/pkg/application/internal/tests/services" + "github.com/wailsapp/wails/v3/pkg/events" +) + +func TestMain(m *testing.M) { + apptest.Main(m) +} + +type ( + Service1 struct{ svctest.StartupShutdowner } + Service2 struct{ svctest.StartupShutdowner } + Service3 struct{ svctest.StartupShutdowner } + Service4 struct{ svctest.StartupShutdowner } + Service5 struct{ svctest.StartupShutdowner } + Service6 struct{ svctest.StartupShutdowner } +) + +func TestServiceStartupShutdownError(t *testing.T) { + var seq atomic.Int64 + + services := []application.Service{ + svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}), + svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq, ShutdownErr: true}), + svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq}), + svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq, StartupErr: true, ShutdownErr: true}), + svctest.Configure(&Service5{}, svctest.Config{Id: 4, T: t, Seq: &seq, StartupErr: true, ShutdownErr: true}), + svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq, StartupErr: true, ShutdownErr: true}), + } + + expectedShutdownErrors := []int{1} + var errCount atomic.Int64 + + var app *application.App + app = apptest.New(t, application.Options{ + Services: services[:3], + ErrorHandler: func(err error) { + var mock *svctest.Error + if !errors.As(err, &mock) { + app.Logger.Error(err.Error()) + return + } + + i := int(errCount.Add(1) - 1) + if i < len(expectedShutdownErrors) && mock.Id == expectedShutdownErrors[i] { + return + } + + cut := min(i, len(expectedShutdownErrors)) + if slices.Contains(expectedShutdownErrors[:cut], mock.Id) { + t.Errorf("Late or duplicate shutdown error for service #%d", mock.Id) + } else if slices.Contains(expectedShutdownErrors[cut:], mock.Id) { + t.Errorf("Early shutdown error for service #%d", mock.Id) + } else { + t.Errorf("Unexpected shutdown error for service #%d", mock.Id) + } + }, + }) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + app.RegisterService(services[3]) + wg.Done() + }() + go func() { + app.RegisterService(services[4]) + wg.Done() + }() + wg.Wait() + + app.RegisterService(services[5]) + + app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) { + t.Errorf("Application started") + app.Quit() + }) + + var mock *svctest.Error + + err := apptest.Run(t, app) + if err != nil { + if !errors.As(err, &mock) { + t.Fatal(err) + } + } + + if mock == nil { + t.Fatal("Wanted error for service #3 or #4, got none") + } else if mock.Id != 3 && mock.Id != 4 { + t.Errorf("Wanted error for service #3 or #4, got #%d", mock.Id) + } + + if ec := errCount.Load(); ec != int64(len(expectedShutdownErrors)) { + t.Errorf("Wrong shutdown error count: wanted %d, got %d", len(expectedShutdownErrors), ec) + } + + if count := seq.Load(); count != 4+3 { + t.Errorf("Wrong startup+shutdown call count: wanted %d+%d, got %d", 4, 3, count) + } + + validate(t, services[0], true, true) + validate(t, services[1], true, true) + validate(t, services[2], true, true) + validate(t, services[3], mock.Id == 3, false) + validate(t, services[4], mock.Id == 4, false) + validate(t, services[5], false, false) +} + +func validate(t *testing.T, svc application.Service, startup bool, shutdown bool) { + id := svc.Instance().(interface{ Id() int }).Id() + startupSeq := svc.Instance().(interface{ StartupSeq() int64 }).StartupSeq() + shutdownSeq := svc.Instance().(interface{ ShutdownSeq() int64 }).ShutdownSeq() + + if startup != (startupSeq != 0) { + if startupSeq == 0 { + t.Errorf("Service #%d did not start up", id) + } else { + t.Errorf("Unexpected startup for service #%d at seq=%d", id, startupSeq) + } + } + + if shutdown != (shutdownSeq != 0) { + if shutdownSeq == 0 { + t.Errorf("Service #%d did not shut down", id) + } else { + t.Errorf("Unexpected shutdown for service #%d at seq=%d", id, shutdownSeq) + } + } +} diff --git a/v3/pkg/application/internal/tests/utils.go b/v3/pkg/application/internal/tests/utils.go new file mode 100644 index 000000000..ef94ef5ba --- /dev/null +++ b/v3/pkg/application/internal/tests/utils.go @@ -0,0 +1,74 @@ +package tests + +import ( + "errors" + "os" + "runtime" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +var appChan chan *application.App = make(chan *application.App, 1) +var errChan chan error = make(chan error, 1) +var endChan chan error = make(chan error, 1) + +func init() { runtime.LockOSThread() } + +func New(t *testing.T, options application.Options) *application.App { + var app *application.App + + app = application.Get() + if app != nil { + return app + } + + if options.Name == "" { + options.Name = t.Name() + } + + errorHandler := options.ErrorHandler + options.ErrorHandler = func(err error) { + if fatal := (*application.FatalError)(nil); errors.As(err, &fatal) { + endChan <- err + select {} // Block forever + } else if errorHandler != nil { + errorHandler(err) + } else { + app.Logger.Error(err.Error()) + } + } + + postShutdown := options.PostShutdown + options.PostShutdown = func() { + if postShutdown != nil { + postShutdown() + } + endChan <- nil + select {} // Block forever + } + + return application.New(options) +} + +func Run(t *testing.T, app *application.App) error { + appChan <- app + select { + case err := <-errChan: + return err + case fatal := <-endChan: + if fatal != nil { + t.Fatal(fatal) + } + return fatal + } +} + +func Main(m *testing.M) { + go func() { + os.Exit(m.Run()) + }() + + errChan <- (<-appChan).Run() + select {} // Block forever +} diff --git a/v3/pkg/application/key_binding_manager.go b/v3/pkg/application/key_binding_manager.go new file mode 100644 index 000000000..64346d740 --- /dev/null +++ b/v3/pkg/application/key_binding_manager.go @@ -0,0 +1,66 @@ +package application + +// KeyBindingManager manages all key binding operations +type KeyBindingManager struct { + app *App +} + +// newKeyBindingManager creates a new KeyBindingManager instance +func newKeyBindingManager(app *App) *KeyBindingManager { + return &KeyBindingManager{ + app: app, + } +} + +// Add adds a key binding +func (kbm *KeyBindingManager) Add(accelerator string, callback func(window Window)) { + kbm.app.keyBindingsLock.Lock() + defer kbm.app.keyBindingsLock.Unlock() + kbm.app.keyBindings[accelerator] = callback +} + +// Remove removes a key binding +func (kbm *KeyBindingManager) Remove(accelerator string) { + kbm.app.keyBindingsLock.Lock() + defer kbm.app.keyBindingsLock.Unlock() + delete(kbm.app.keyBindings, accelerator) +} + +// Process processes a key binding and returns true if handled +func (kbm *KeyBindingManager) Process(accelerator string, window Window) bool { + kbm.app.keyBindingsLock.RLock() + callback, exists := kbm.app.keyBindings[accelerator] + kbm.app.keyBindingsLock.RUnlock() + + if exists && callback != nil { + callback(window) + return true + } + return false +} + +// KeyBinding represents a key binding with its accelerator and callback +type KeyBinding struct { + Accelerator string + Callback func(window Window) +} + +// GetAll returns all registered key bindings as a slice +func (kbm *KeyBindingManager) GetAll() []*KeyBinding { + kbm.app.keyBindingsLock.RLock() + defer kbm.app.keyBindingsLock.RUnlock() + + result := make([]*KeyBinding, 0, len(kbm.app.keyBindings)) + for accelerator, callback := range kbm.app.keyBindings { + result = append(result, &KeyBinding{ + Accelerator: accelerator, + Callback: callback, + }) + } + return result +} + +// HandleWindowKeyEvent handles window key events (internal use) +func (kbm *KeyBindingManager) handleWindowKeyEvent(event *windowKeyEvent) { + kbm.app.handleWindowKeyEvent(event) +} diff --git a/v3/pkg/application/keys.go b/v3/pkg/application/keys.go new file mode 100644 index 000000000..375b76289 --- /dev/null +++ b/v3/pkg/application/keys.go @@ -0,0 +1,220 @@ +package application + +import ( + "fmt" + "runtime" + "slices" + "strconv" + "strings" +) + +// modifier is actually a string +type modifier int + +const ( + // CmdOrCtrlKey represents Command on Mac and Control on other platforms + CmdOrCtrlKey modifier = 0 << iota + // OptionOrAltKey represents Option on Mac and Alt on other platforms + OptionOrAltKey modifier = 1 << iota + // ShiftKey represents the shift key on all systems + ShiftKey modifier = 2 << iota + // SuperKey represents Command on Mac and the Windows key on the other platforms + SuperKey modifier = 3 << iota + // ControlKey represents the control key on all systems + ControlKey modifier = 4 << iota +) + +func (m modifier) String() string { + return modifierStringMap[runtime.GOOS][m] +} + +var modifierStringMap = map[string]map[modifier]string{ + "windows": { + CmdOrCtrlKey: "Ctrl", + ControlKey: "Ctrl", + OptionOrAltKey: "Alt", + ShiftKey: "Shift", + SuperKey: "Win", + }, + "darwin": { + CmdOrCtrlKey: "Cmd", + ControlKey: "Ctrl", + OptionOrAltKey: "Option", + ShiftKey: "Shift", + SuperKey: "Cmd", + }, + "linux": { + CmdOrCtrlKey: "Ctrl", + ControlKey: "Ctrl", + OptionOrAltKey: "Alt", + ShiftKey: "Shift", + SuperKey: "Super", + }, +} + +var modifierMap = map[string]modifier{ + "cmdorctrl": CmdOrCtrlKey, + "cmd": CmdOrCtrlKey, + "command": CmdOrCtrlKey, + "ctrl": ControlKey, + "optionoralt": OptionOrAltKey, + "alt": OptionOrAltKey, + "option": OptionOrAltKey, + "shift": ShiftKey, + "super": SuperKey, +} + +// accelerator holds the keyboard shortcut for a menu item +type accelerator struct { + Key string + Modifiers []modifier +} + +func (a *accelerator) clone() *accelerator { + result := *a + return &result +} + +func (a *accelerator) String() string { + var result []string + // Sort modifiers + for _, modifier := range a.Modifiers { + result = append(result, modifier.String()) + } + slices.Sort(result) + if len(a.Key) > 0 { + result = append(result, strings.ToUpper(a.Key)) + } + return strings.Join(result, "+") +} + +var namedKeys = map[string]struct{}{ + "backspace": {}, + "tab": {}, + "return": {}, + "enter": {}, + "escape": {}, + "left": {}, + "right": {}, + "up": {}, + "down": {}, + "space": {}, + "delete": {}, + "home": {}, + "end": {}, + "page up": {}, + "page down": {}, + "f1": {}, + "f2": {}, + "f3": {}, + "f4": {}, + "f5": {}, + "f6": {}, + "f7": {}, + "f8": {}, + "f9": {}, + "f10": {}, + "f11": {}, + "f12": {}, + "f13": {}, + "f14": {}, + "f15": {}, + "f16": {}, + "f17": {}, + "f18": {}, + "f19": {}, + "f20": {}, + "f21": {}, + "f22": {}, + "f23": {}, + "f24": {}, + "f25": {}, + "f26": {}, + "f27": {}, + "f28": {}, + "f29": {}, + "f30": {}, + "f31": {}, + "f32": {}, + "f33": {}, + "f34": {}, + "f35": {}, + "numlock": {}, +} + +func parseKey(key string) (string, bool) { + + // Lowercase! + key = strings.ToLower(key) + + // Check special case + if key == "plus" { + return "+", true + } + + // Handle named keys + _, namedKey := namedKeys[key] + if namedKey { + return key, true + } + + // Check we only have a single character + if len(key) != 1 { + return "", false + } + + runeKey := rune(key[0]) + + // This may be too inclusive + if strconv.IsPrint(runeKey) { + return key, true + } + + return "", false + +} + +// parseAccelerator parses a string into an accelerator +func parseAccelerator(shortcut string) (*accelerator, error) { + + var result accelerator + + // Split the shortcut by + + components := strings.Split(shortcut, "+") + + // If we only have one it should be a key + // We require components + if len(components) == 0 { + return nil, fmt.Errorf("no components given to validateComponents") + } + + modifiers := map[modifier]struct{}{} + + // Check components + for index, component := range components { + + // If last component + if index == len(components)-1 { + processedKey, validKey := parseKey(component) + if !validKey { + return nil, fmt.Errorf("'%s' is not a valid key", component) + } + result.Key = strings.ToLower(processedKey) + continue + } + + // Not last component - needs to be modifier + lowercaseComponent := strings.ToLower(component) + thisModifier, valid := modifierMap[lowercaseComponent] + if !valid { + return nil, fmt.Errorf("'%s' is not a valid modifier", component) + } + // Save this data + modifiers[thisModifier] = struct{}{} + } + // return the keys as a slice + for thisModifier := range modifiers { + result.Modifiers = append(result.Modifiers, thisModifier) + } + return &result, nil +} diff --git a/v3/pkg/application/keys_darwin.go b/v3/pkg/application/keys_darwin.go new file mode 100644 index 000000000..42e1c4686 --- /dev/null +++ b/v3/pkg/application/keys_darwin.go @@ -0,0 +1,28 @@ +//go:build darwin + +package application + +const ( + NSEventModifierFlagShift = 1 << 17 // Set if Shift key is pressed. + NSEventModifierFlagControl = 1 << 18 // Set if Control key is pressed. + NSEventModifierFlagOption = 1 << 19 // Set if Option or Alternate key is pressed. + NSEventModifierFlagCommand = 1 << 20 // Set if Command key is pressed. +) + +// macModifierMap maps accelerator modifiers to macOS modifiers. +var macModifierMap = map[modifier]int{ + CmdOrCtrlKey: NSEventModifierFlagCommand, + ControlKey: NSEventModifierFlagControl, + OptionOrAltKey: NSEventModifierFlagOption, + ShiftKey: NSEventModifierFlagShift, + SuperKey: NSEventModifierFlagCommand, +} + +// toMacModifier converts the accelerator to a macOS modifier. +func toMacModifier(modifiers []modifier) int { + result := 0 + for _, modifier := range modifiers { + result |= macModifierMap[modifier] + } + return result +} diff --git a/v3/pkg/application/keys_linux.go b/v3/pkg/application/keys_linux.go new file mode 100644 index 000000000..e7b44e380 --- /dev/null +++ b/v3/pkg/application/keys_linux.go @@ -0,0 +1,165 @@ +//go:build linux + +package application + +var VirtualKeyCodes = map[uint]string{ + 0xff08: "backspace", + 0xff09: "tab", + 0xff0a: "linefeed", + 0xff0b: "clear", + 0xff0d: "return", + 0xff13: "pause", + 0xff14: "scrolllock", + 0xff15: "sysreq", + 0xff1b: "escape", + 0xffff: "delete", + 0xff50: "home", + 0xff51: "left", + + 0xffe1: "lshift", + 0xffe2: "rshift", + 0xffe3: "lcontrol", + 0xffe4: "rcontrol", + 0xffeb: "lmeta", + 0xffec: "rmeta", + 0xffed: "lalt", + 0xffee: "ralt", + // Multi-Lang + 0xff21: "kanji", + 0xff22: "muhenkan", + 0xff24: "henkan", + 0xff25: "hiragana", + 0xff26: "katakana", + 0xff27: "hiragana/katakana", + 0xff28: "zenkaku", + 0xff29: "hankaku", + 0xff2a: "zenkaku/hankaku", + 0xff2b: "touroku", + 0xff2c: "massyo", + 0xff2d: "kana lock", + 0xff2e: "kana shift", + 0xff2f: "eisu shift", + 0xff30: "eisu toggle", + 0xff37: "kanji bangou", + + // Directions + 0xff52: "up", + 0xff53: "right", + 0xff54: "down", + 0xff55: "pageup", + 0xff56: "pagedown", + 0xff57: "end", + 0xff58: "begin", + + + // Alphabet + 0x41: "a", + 0x42: "b", + 0x43: "c", + 0x44: "d", + 0x45: "e", + 0x46: "f", + 0x47: "g", + 0x48: "h", + 0x49: "i", + 0x4a: "j", + 0x4b: "k", + 0x4c: "l", + 0x4d: "m", + 0x4e: "n", + 0x4f: "o", + 0x50: "p", + 0x51: "q", + 0x52: "r", + 0x53: "s", + 0x54: "t", + 0x55: "u", + 0x56: "v", + 0x57: "w", + 0x58: "x", + 0x59: "y", + 0x5a: "z", + + 0x5b: "lbracket", + 0x5c: "backslash", + 0x5d: "rbracket", + 0x5e: "caret", + 0x5f: "underscore", + 0x60: "grave", + // Capital Alphabet + 0x61: "a", + 0x62: "b", + 0x63: "c", + 0x64: "d", + 0x65: "e", + 0x66: "f", + 0x67: "g", + 0x68: "h", + 0x69: "i", + 0x6a: "j", + 0x6b: "k", + 0x6c: "l", + 0x6d: "m", + 0x6e: "n", + 0x6f: "o", + 0x70: "p", + 0x71: "q", + 0x72: "r", + 0x73: "s", + 0x74: "t", + 0x75: "u", + 0x76: "v", + 0x77: "w", + 0x78: "x", + 0x79: "y", + 0x7a: "z", + 0x7b: "lbrace", + 0x7c: "pipe", + 0x7d: "rbrace", + 0x7e: "tilde", + 0xa1: "exclam", + 0xa2: "cent", + 0xa3: "sterling", + 0xa4: "currency", + 0xa5: "yen", + 0xa6: "brokenbar", + 0xa7: "section", + 0xa8: "diaeresis", + 0xa9: "copyright", + 0xaa: "ordfeminine", + 0xab: "guillemotleft", + 0xad: "hyphen", + 0xae: "registered", + 0xaf: "macron", + 0xb0: "degree", + 0xb1: "plusminus", + 0xb2: "twosuperior", + + // Function Keys + 0xffbe: "f1", + 0xffbf: "f2", + 0xffc0: "f3", + 0xffc1: "f4", + 0xffc2: "f5", + 0xffc3: "f6", + 0xffc4: "f7", + 0xffc5: "f8", + 0xffc6: "f9", + 0xffc7: "f10", + 0xffc8: "f11", + 0xffc9: "f12", + 0xffca: "f13", + 0xffcb: "f14", + 0xffcc: "f15", + 0xffcd: "f16", + 0xffce: "f17", + 0xffcf: "f18", + 0xffd0: "f19", + 0xffd1: "f20", + 0xffd2: "f21", + 0xffd3: "f22", + 0xffd4: "f23", + 0xffd5: "f24", + + +} diff --git a/v3/pkg/application/keys_windows.go b/v3/pkg/application/keys_windows.go new file mode 100644 index 000000000..8011a837d --- /dev/null +++ b/v3/pkg/application/keys_windows.go @@ -0,0 +1,230 @@ +//go:build windows + +package application + +var VirtualKeyCodes = map[uint]string{ + 0x01: "lbutton", + 0x02: "rbutton", + 0x03: "cancel", + 0x04: "mbutton", + 0x05: "xbutton1", + 0x06: "xbutton2", + 0x08: "back", + 0x09: "tab", + 0x0C: "clear", + 0x0D: "return", + 0x10: "shift", + 0x11: "ctrl", + 0x12: "menu", + 0x13: "pause", + 0x14: "capital", + 0x15: "kana", + 0x17: "junja", + 0x18: "final", + 0x19: "hanja", + 0x1B: "escape", + 0x1C: "convert", + 0x1D: "nonconvert", + 0x1E: "accept", + 0x1F: "modechange", + 0x20: "space", + 0x21: "prior", + 0x22: "next", + 0x23: "end", + 0x24: "home", + 0x25: "left", + 0x26: "up", + 0x27: "right", + 0x28: "down", + 0x29: "select", + 0x2A: "print", + 0x2B: "execute", + 0x2C: "snapshot", + 0x2D: "insert", + 0x2E: "delete", + 0x2F: "help", + 0x30: "0", + 0x31: "1", + 0x32: "2", + 0x33: "3", + 0x34: "4", + 0x35: "5", + 0x36: "6", + 0x37: "7", + 0x38: "8", + 0x39: "9", + 0x41: "a", + 0x42: "b", + 0x43: "c", + 0x44: "d", + 0x45: "e", + 0x46: "f", + 0x47: "g", + 0x48: "h", + 0x49: "i", + 0x4A: "j", + 0x4B: "k", + 0x4C: "l", + 0x4D: "m", + 0x4E: "n", + 0x4F: "o", + 0x50: "p", + 0x51: "q", + 0x52: "r", + 0x53: "s", + 0x54: "t", + 0x55: "u", + 0x56: "v", + 0x57: "w", + 0x58: "x", + 0x59: "y", + 0x5A: "z", + 0x5B: "lwin", + 0x5C: "rwin", + 0x5D: "apps", + 0x5F: "sleep", + 0x60: "numpad0", + 0x61: "numpad1", + 0x62: "numpad2", + 0x63: "numpad3", + 0x64: "numpad4", + 0x65: "numpad5", + 0x66: "numpad6", + 0x67: "numpad7", + 0x68: "numpad8", + 0x69: "numpad9", + 0x6A: "multiply", + 0x6B: "add", + 0x6C: "separator", + 0x6D: "subtract", + 0x6E: "decimal", + 0x6F: "divide", + 0x70: "f1", + 0x71: "f2", + 0x72: "f3", + 0x73: "f4", + 0x74: "f5", + 0x75: "f6", + 0x76: "f7", + 0x77: "f8", + 0x78: "f9", + 0x79: "f10", + 0x7A: "f11", + 0x7B: "f12", + 0x7C: "f13", + 0x7D: "f14", + 0x7E: "f15", + 0x7F: "f16", + 0x80: "f17", + 0x81: "f18", + 0x82: "f19", + 0x83: "f20", + 0x84: "f21", + 0x85: "f22", + 0x86: "f23", + 0x87: "f24", + 0x88: "navigation_view", + 0x89: "navigation_menu", + 0x8A: "navigation_up", + 0x8B: "navigation_down", + 0x8C: "navigation_left", + 0x8D: "navigation_right", + 0x8E: "navigation_accept", + 0x8F: "navigation_cancel", + 0x90: "numlock", + 0x91: "scroll", + 0x92: "oem_nec_equal", + 0x93: "oem_fj_masshou", + 0x94: "oem_fj_touroku", + 0x95: "oem_fj_loya", + 0x96: "oem_fj_roya", + 0xA0: "lshift", + 0xA1: "rshift", + 0xA2: "lcontrol", + 0xA3: "rcontrol", + 0xA4: "lmenu", + 0xA5: "rmenu", + 0xA6: "browser_back", + 0xA7: "browser_forward", + 0xA8: "browser_refresh", + 0xA9: "browser_stop", + 0xAA: "browser_search", + 0xAB: "browser_favorites", + 0xAC: "browser_home", + 0xAD: "volume_mute", + 0xAE: "volume_down", + 0xAF: "volume_up", + 0xB0: "media_next_track", + 0xB1: "media_prev_track", + 0xB2: "media_stop", + 0xB3: "media_play_pause", + 0xB4: "launch_mail", + 0xB5: "launch_media_select", + 0xB6: "launch_app1", + 0xB7: "launch_app2", + 0xBA: "oem_1", + 0xBB: "oem_plus", + 0xBC: "oem_comma", + 0xBD: "oem_minus", + 0xBE: "oem_period", + 0xBF: "oem_2", + 0xC0: "oem_3", + 0xC3: "gamepad_a", + 0xC4: "gamepad_b", + 0xC5: "gamepad_x", + 0xC6: "gamepad_y", + 0xC7: "gamepad_right_shoulder", + 0xC8: "gamepad_left_shoulder", + 0xC9: "gamepad_left_trigger", + 0xCA: "gamepad_right_trigger", + 0xCB: "gamepad_dpad_up", + 0xCC: "gamepad_dpad_down", + 0xCD: "gamepad_dpad_left", + 0xCE: "gamepad_dpad_right", + 0xCF: "gamepad_menu", + 0xD0: "gamepad_view", + 0xD1: "gamepad_left_thumbstick_button", + 0xD2: "gamepad_right_thumbstick_button", + 0xD3: "gamepad_left_thumbstick_up", + 0xD4: "gamepad_left_thumbstick_down", + 0xD5: "gamepad_left_thumbstick_right", + 0xD6: "gamepad_left_thumbstick_left", + 0xD7: "gamepad_right_thumbstick_up", + 0xD8: "gamepad_right_thumbstick_down", + 0xD9: "gamepad_right_thumbstick_right", + 0xDA: "gamepad_right_thumbstick_left", + 0xDB: "oem_4", + 0xDC: "oem_5", + 0xDD: "oem_6", + 0xDE: "oem_7", + 0xDF: "oem_8", + 0xE1: "oem_ax", + 0xE2: "oem_102", + 0xE3: "ico_help", + 0xE4: "ico_00", + 0xE5: "processkey", + 0xE6: "ico_clear", + 0xE7: "packet", + 0xE9: "oem_reset", + 0xEA: "oem_jump", + 0xEB: "oem_pa1", + 0xEC: "oem_pa2", + 0xED: "oem_pa3", + 0xEE: "oem_wsctrl", + 0xEF: "oem_cusel", + 0xF0: "oem_attn", + 0xF1: "oem_finish", + 0xF2: "oem_copy", + 0xF3: "oem_auto", + 0xF4: "oem_enlw", + 0xF5: "oem_backtab", + 0xF6: "attn", + 0xF7: "crsel", + 0xF8: "exsel", + 0xF9: "ereof", + 0xFA: "play", + 0xFB: "zoom", + 0xFC: "noname", + 0xFD: "pa1", + 0xFE: "oem_clear", +} diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go new file mode 100644 index 000000000..b1e4e2152 --- /dev/null +++ b/v3/pkg/application/linux_cgo.go @@ -0,0 +1,1909 @@ +//go:build linux && cgo + +package application + +import ( + "fmt" + "regexp" + "strings" + "sync" + "time" + "unsafe" + + "github.com/wailsapp/wails/v3/internal/assetserver/webview" + + "github.com/wailsapp/wails/v3/pkg/events" +) + +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 gdk-3.0 + +#include +#include +#include +#include +#include +#include +#ifdef G_APPLICATION_DEFAULT_FLAGS + #define APPLICATION_DEFAULT_FLAGS G_APPLICATION_DEFAULT_FLAGS +#else + #define APPLICATION_DEFAULT_FLAGS G_APPLICATION_FLAGS_NONE +#endif + +typedef struct CallbackID +{ + unsigned int value; +} CallbackID; + +extern void dispatchOnMainThreadCallback(unsigned int); + +static gboolean dispatchCallback(gpointer data) { + struct CallbackID *args = data; + unsigned int cid = args->value; + dispatchOnMainThreadCallback(cid); + free(args); + + return G_SOURCE_REMOVE; +}; + +static void dispatchOnMainThread(unsigned int id) { + CallbackID *args = malloc(sizeof(CallbackID)); + args->value = id; + g_idle_add((GSourceFunc)dispatchCallback, (gpointer)args); +} + +typedef struct WindowEvent { + uint id; + uint event; +} WindowEvent; + +static void save_window_id(void *object, uint value) +{ + g_object_set_data((GObject *)object, "windowid", GUINT_TO_POINTER((guint)value)); +} + +static guint get_window_id(void *object) +{ + return GPOINTER_TO_UINT(g_object_get_data((GObject *)object, "windowid")); +} + +// exported below +void activateLinux(gpointer data); +extern void emit(WindowEvent* data); +extern gboolean handleConfigureEvent(GtkWidget*, GdkEventConfigure*, uintptr_t); +extern gboolean handleDeleteEvent(GtkWidget*, GdkEvent*, uintptr_t); +extern gboolean handleFocusEvent(GtkWidget*, GdkEvent*, uintptr_t); +extern void handleLoadChanged(WebKitWebView*, WebKitLoadEvent, uintptr_t); +void handleClick(void*); +extern gboolean onButtonEvent(GtkWidget *widget, GdkEventButton *event, uintptr_t user_data); +extern gboolean onMenuButtonEvent(GtkWidget *widget, GdkEventButton *event, uintptr_t user_data); +extern void onUriList(char **extracted, gint x, gint y, gpointer data); +extern gboolean onKeyPressEvent (GtkWidget *widget, GdkEventKey *event, uintptr_t user_data); +extern void onProcessRequest(WebKitURISchemeRequest *request, uintptr_t user_data); +extern void sendMessageToBackend(WebKitUserContentManager *contentManager, WebKitJavascriptResult *result, void *data); +// exported below (end) + +static void signal_connect(void *widget, char *event, void *cb, void* data) { + // g_signal_connect is a macro and can't be called directly + g_signal_connect(widget, event, cb, data); +} + +static WebKitWebView* webkit_web_view(GtkWidget *webview) { + return WEBKIT_WEB_VIEW(webview); +} + +static void* new_message_dialog(GtkWindow *parent, const gchar *msg, int dialogType, bool hasButtons) { + // gtk_message_dialog_new is variadic! Can't call from cgo directly + GtkWidget *dialog; + int buttonMask; + + // buttons will be added after creation + buttonMask = GTK_BUTTONS_OK; + if (hasButtons) { + buttonMask = GTK_BUTTONS_NONE; + } + + dialog = gtk_message_dialog_new( + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + dialogType, + buttonMask, + "%s", + msg); + + // g_signal_connect_swapped (dialog, + // "response", + // G_CALLBACK (callback), + // dialog); + return dialog; +}; + +extern void messageDialogCB(gint button); + +static void* gtkFileChooserDialogNew(char* title, GtkWindow* window, GtkFileChooserAction action, char* cancelLabel, char* acceptLabel) { + // gtk_file_chooser_dialog_new is variadic! Can't call from cgo directly + return (GtkFileChooser*)gtk_file_chooser_dialog_new( + title, + window, + action, + cancelLabel, + GTK_RESPONSE_CANCEL, + acceptLabel, + GTK_RESPONSE_ACCEPT, + NULL); +} + +typedef struct Screen { + const char* id; + const char* name; + int p_width; + int p_height; + int width; + int height; + int x; + int y; + int w_width; + int w_height; + int w_x; + int w_y; + float scaleFactor; + double rotation; + bool isPrimary; +} Screen; + +// CREDIT: https://github.com/rainycape/magick +#include +#include +#include +#include + +static void fix_signal(int signum) { + struct sigaction st; + + if (sigaction(signum, NULL, &st) < 0) { + goto fix_signal_error; + } + st.sa_flags |= SA_ONSTACK; + if (sigaction(signum, &st, NULL) < 0) { + goto fix_signal_error; + } + return; +fix_signal_error: + fprintf(stderr, "error fixing handler for signal %d, please " + "report this issue to " + "https://github.com/wailsapp/wails: %s\n", + signum, strerror(errno)); +} + +static void install_signal_handlers() { + #if defined(SIGCHLD) + fix_signal(SIGCHLD); + #endif + #if defined(SIGHUP) + fix_signal(SIGHUP); + #endif + #if defined(SIGINT) + fix_signal(SIGINT); + #endif + #if defined(SIGQUIT) + fix_signal(SIGQUIT); + #endif + #if defined(SIGABRT) + fix_signal(SIGABRT); + #endif + #if defined(SIGFPE) + fix_signal(SIGFPE); + #endif + #if defined(SIGTERM) + fix_signal(SIGTERM); + #endif + #if defined(SIGBUS) + fix_signal(SIGBUS); + #endif + #if defined(SIGSEGV) + fix_signal(SIGSEGV); + #endif + #if defined(SIGXCPU) + fix_signal(SIGXCPU); + #endif + #if defined(SIGXFSZ) + fix_signal(SIGXFSZ); + #endif +} + +static int GetNumScreens(){ + return 0; +} + +static void on_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, + GtkSelectionData *selection_data, guint target_type, guint time, + gpointer data) +{ + gint length = gtk_selection_data_get_length(selection_data); + + if (length < 0) + { + g_print("DnD failed!\n"); + gtk_drag_finish(context, FALSE, FALSE, time); + } + + gchar *uri_data = (gchar *)gtk_selection_data_get_data(selection_data); + gchar **uri_list = g_uri_list_extract_uris(uri_data); + + onUriList(uri_list, x, y, data); + + g_strfreev(uri_list); + gtk_drag_finish(context, TRUE, TRUE, time); +} + +// drag and drop tutorial: https://wiki.gnome.org/Newcomers/OldDragNDropTutorial +static void enableDND(GtkWidget *widget, gpointer data) +{ + GtkTargetEntry *target = gtk_target_entry_new("text/uri-list", 0, 0); + gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, target, 1, GDK_ACTION_COPY); + + signal_connect(widget, "drag-data-received", on_data_received, data); +} +*/ +import "C" + +// Calloc handles alloc/dealloc of C data +type Calloc struct { + pool []unsafe.Pointer +} + +// NewCalloc creates a new allocator +func NewCalloc() Calloc { + return Calloc{} +} + +// String creates a new C string and retains a reference to it +func (c Calloc) String(in string) *C.char { + result := C.CString(in) + c.pool = append(c.pool, unsafe.Pointer(result)) + return result +} + +// Free frees all allocated C memory +func (c Calloc) Free() { + for _, str := range c.pool { + C.free(str) + } + c.pool = []unsafe.Pointer{} +} + +type windowPointer *C.GtkWindow +type identifier C.uint +type pointer unsafe.Pointer +type GSList C.GSList +type GSListPointer *GSList + +// getLinuxWebviewWindow safely extracts a linuxWebviewWindow from a Window interface +// Returns nil if the window is not a WebviewWindow or not a Linux implementation +func getLinuxWebviewWindow(window Window) *linuxWebviewWindow { + if window == nil { + return nil + } + + webviewWindow, ok := window.(*WebviewWindow) + if !ok { + return nil + } + + lw, ok := webviewWindow.impl.(*linuxWebviewWindow) + if !ok { + return nil + } + + return lw +} + +var ( + nilPointer pointer = nil + nilRadioGroup GSListPointer = nil +) + +var ( + gtkSignalToMenuItem map[uint]*MenuItem + mainThreadId *C.GThread +) + +var registerURIScheme sync.Once + +func init() { + gtkSignalToMenuItem = map[uint]*MenuItem{} + + mainThreadId = C.g_thread_self() +} + +// mainthread stuff +func dispatchOnMainThread(id uint) { + C.dispatchOnMainThread(C.uint(id)) +} + +//export dispatchOnMainThreadCallback +func dispatchOnMainThreadCallback(callbackID C.uint) { + executeOnMainThread(uint(callbackID)) +} + +//export activateLinux +func activateLinux(data pointer) { + processApplicationEvent(C.uint(events.Linux.ApplicationStartup), data) +} + +//export processApplicationEvent +func processApplicationEvent(eventID C.uint, data pointer) { + event := newApplicationEvent(events.ApplicationEventType(eventID)) + + //if data != nil { + // dataCStrJSON := C.serializationNSDictionary(data) + // if dataCStrJSON != nil { + // defer C.free(unsafe.Pointer(dataCStrJSON)) + // + // dataJSON := C.GoString(dataCStrJSON) + // var result map[string]any + // err := json.Unmarshal([]byte(dataJSON), &result) + // + // if err != nil { + // panic(err) + // } + // + // event.Context().setData(result) + // } + //} + + switch event.Id { + case uint(events.Linux.SystemThemeChanged): + isDark := globalApplication.Env.IsDarkMode() + event.Context().setIsDarkMode(isDark) + } + applicationEvents <- event +} + +func isOnMainThread() bool { + threadId := C.g_thread_self() + return threadId == mainThreadId +} + +// implementation below +func appName() string { + name := C.g_get_application_name() + defer C.free(unsafe.Pointer(name)) + return C.GoString(name) +} + +func appNew(name string) pointer { + C.install_signal_handlers() + + // prevent leading number + if matched, _ := regexp.MatchString(`^\d+`, name); matched { + name = fmt.Sprintf("_%s", name) + } + name = strings.Replace(name, "(", "_", -1) + name = strings.Replace(name, ")", "_", -1) + appId := fmt.Sprintf("org.wails.%s", name) + nameC := C.CString(appId) + defer C.free(unsafe.Pointer(nameC)) + return pointer(C.gtk_application_new(nameC, C.APPLICATION_DEFAULT_FLAGS)) +} + +func setProgramName(prgName string) { + cPrgName := C.CString(prgName) + defer C.free(unsafe.Pointer(cPrgName)) + C.g_set_prgname(cPrgName) +} + +func appRun(app pointer) error { + application := (*C.GApplication)(app) + //TODO: Only set this if we configure it to do so + C.g_application_hold(application) // allows it to run without a window + + signal := C.CString("activate") + defer C.free(unsafe.Pointer(signal)) + C.signal_connect(unsafe.Pointer(application), signal, C.activateLinux, nil) + status := C.g_application_run(application, 0, nil) + C.g_application_release(application) + C.g_object_unref(C.gpointer(app)) + + var err error + if status != 0 { + err = fmt.Errorf("exit code: %d", status) + } + return err +} + +func appDestroy(application pointer) { + C.g_application_quit((*C.GApplication)(application)) +} + +func (w *linuxWebviewWindow) contextMenuSignals(menu pointer) { + c := NewCalloc() + defer c.Free() + winID := unsafe.Pointer(uintptr(C.uint(w.parent.ID()))) + C.signal_connect(unsafe.Pointer(menu), c.String("button-release-event"), C.onMenuButtonEvent, winID) +} + +func (w *linuxWebviewWindow) contextMenuShow(menu pointer, data *ContextMenuData) { + geometry := C.GdkRectangle{ + x: C.int(data.X), + y: C.int(data.Y), + } + event := C.GdkEvent{} + gdkWindow := C.gtk_widget_get_window(w.gtkWidget()) + C.gtk_menu_popup_at_rect( + (*C.GtkMenu)(menu), + gdkWindow, + (*C.GdkRectangle)(&geometry), + C.GDK_GRAVITY_NORTH_WEST, + C.GDK_GRAVITY_NORTH_WEST, + (*C.GdkEvent)(&event), + ) + w.ctxMenuOpened = true +} + +func (a *linuxApp) getCurrentWindowID() uint { + // TODO: Add extra metadata to window and use it! + window := (*C.GtkWindow)(C.gtk_application_get_active_window((*C.GtkApplication)(a.application))) + if window == nil { + return uint(1) + } + identifier, ok := a.windowMap[window] + if ok { + return identifier + } + // FIXME: Should we panic here if not found? + return uint(1) +} + +func (a *linuxApp) getWindows() []pointer { + result := []pointer{} + windows := C.gtk_application_get_windows((*C.GtkApplication)(a.application)) + for { + result = append(result, pointer(windows.data)) + windows = windows.next + if windows == nil { + return result + } + } +} + +func (a *linuxApp) hideAllWindows() { + for _, window := range a.getWindows() { + C.gtk_widget_hide((*C.GtkWidget)(window)) + } +} + +func (a *linuxApp) showAllWindows() { + for _, window := range a.getWindows() { + C.gtk_window_present((*C.GtkWindow)(window)) + } +} + +func (a *linuxApp) setIcon(icon []byte) { + gbytes := C.g_bytes_new_static(C.gconstpointer(unsafe.Pointer(&icon[0])), C.ulong(len(icon))) + stream := C.g_memory_input_stream_new_from_bytes(gbytes) + var gerror *C.GError + pixbuf := C.gdk_pixbuf_new_from_stream(stream, nil, &gerror) + if gerror != nil { + a.parent.error("failed to load application icon: %s", C.GoString(gerror.message)) + C.g_error_free(gerror) + return + } + + a.icon = pointer(pixbuf) +} + +// Clipboard +func clipboardGet() string { + clip := C.gtk_clipboard_get(C.GDK_SELECTION_CLIPBOARD) + text := C.gtk_clipboard_wait_for_text(clip) + return C.GoString(text) +} + +func clipboardSet(text string) { + cText := C.CString(text) + clip := C.gtk_clipboard_get(C.GDK_SELECTION_CLIPBOARD) + C.gtk_clipboard_set_text(clip, cText, -1) + + clip = C.gtk_clipboard_get(C.GDK_SELECTION_PRIMARY) + C.gtk_clipboard_set_text(clip, cText, -1) + C.free(unsafe.Pointer(cText)) +} + +// Menu +func menuAddSeparator(menu *Menu) { + C.gtk_menu_shell_append( + (*C.GtkMenuShell)((menu.impl).(*linuxMenu).native), + C.gtk_separator_menu_item_new()) +} + +func menuAppend(parent *Menu, menu *MenuItem) { + C.gtk_menu_shell_append( + (*C.GtkMenuShell)((parent.impl).(*linuxMenu).native), + (*C.GtkWidget)((menu.impl).(*linuxMenuItem).native), + ) + /* gtk4 + C.gtk_menu_item_set_submenu( + (*C.struct__GtkMenuItem)((menu.impl).(*linuxMenuItem).native), + (*C.struct__GtkWidget)((parent.impl).(*linuxMenu).native), + ) + */ +} + +func menuBarNew() pointer { + return pointer(C.gtk_menu_bar_new()) +} + +func menuNew() pointer { + return pointer(C.gtk_menu_new()) +} + +func menuSetSubmenu(item *MenuItem, menu *Menu) { + C.gtk_menu_item_set_submenu( + (*C.GtkMenuItem)((item.impl).(*linuxMenuItem).native), + (*C.GtkWidget)((menu.impl).(*linuxMenu).native)) +} + +func menuGetRadioGroup(item *linuxMenuItem) *GSList { + return (*GSList)(C.gtk_radio_menu_item_get_group((*C.GtkRadioMenuItem)(item.native))) +} + +//export handleClick +func handleClick(idPtr unsafe.Pointer) { + ident := C.CString("id") + defer C.free(unsafe.Pointer(ident)) + value := C.g_object_get_data((*C.GObject)(idPtr), ident) + id := uint(*(*C.uint)(value)) + item, ok := gtkSignalToMenuItem[id] + if !ok { + return + } + switch item.itemType { + case text, checkbox: + menuItemClicked <- item.id + case radio: + menuItem := (item.impl).(*linuxMenuItem) + if menuItem.isChecked() { + menuItemClicked <- item.id + } + } +} + +func attachMenuHandler(item *MenuItem) uint { + signal := C.CString("activate") + defer C.free(unsafe.Pointer(signal)) + impl := (item.impl).(*linuxMenuItem) + widget := impl.native + flags := C.GConnectFlags(0) + handlerId := C.g_signal_connect_object( + C.gpointer(widget), + signal, + C.GCallback(C.handleClick), + C.gpointer(widget), + flags) + + id := C.uint(item.id) + ident := C.CString("id") + defer C.free(unsafe.Pointer(ident)) + C.g_object_set_data( + (*C.GObject)(widget), + ident, + C.gpointer(&id), + ) + + gtkSignalToMenuItem[item.id] = item + return uint(handlerId) +} + +// menuItem +func menuItemChecked(widget pointer) bool { + if C.gtk_check_menu_item_get_active((*C.GtkCheckMenuItem)(widget)) == C.int(1) { + return true + } + return false +} + +func menuItemNew(label string, bitmap []byte) pointer { + return menuItemAddProperties(C.gtk_menu_item_new(), label, bitmap) +} + +func menuItemDestroy(widget pointer) { + C.gtk_widget_destroy((*C.GtkWidget)(widget)) +} + +func menuItemAddProperties(menuItem *C.GtkWidget, label string, bitmap []byte) pointer { + /* + // FIXME: Support accelerator configuration + activate := C.CString("activate") + defer C.free(unsafe.Pointer(activate)) + accelGroup := C.gtk_accel_group_new() + C.gtk_widget_add_accelerator(menuItem, activate, accelGroup, + C.GDK_KEY_m, C.GDK_CONTROL_MASK, C.GTK_ACCEL_VISIBLE) + */ + cLabel := C.CString(label) + defer C.free(unsafe.Pointer(cLabel)) + lbl := unsafe.Pointer(C.gtk_accel_label_new(cLabel)) + C.gtk_label_set_use_underline((*C.GtkLabel)(lbl), 1) + C.gtk_label_set_xalign((*C.GtkLabel)(lbl), 0.0) + C.gtk_accel_label_set_accel_widget( + (*C.GtkAccelLabel)(lbl), + (*C.GtkWidget)(unsafe.Pointer(menuItem))) + + box := C.gtk_box_new(C.GTK_ORIENTATION_HORIZONTAL, 6) + if img, err := pngToImage(bitmap); err == nil { + gbytes := C.g_bytes_new_static(C.gconstpointer(unsafe.Pointer(&img.Pix[0])), + C.ulong(len(img.Pix))) + defer C.g_bytes_unref(gbytes) + pixBuf := C.gdk_pixbuf_new_from_bytes( + gbytes, + C.GDK_COLORSPACE_RGB, + 1, // has_alpha + 8, + C.int(img.Bounds().Dx()), + C.int(img.Bounds().Dy()), + C.int(img.Stride), + ) + image := C.gtk_image_new_from_pixbuf(pixBuf) + C.gtk_widget_set_visible((*C.GtkWidget)(image), C.gboolean(1)) + C.gtk_container_add( + (*C.GtkContainer)(unsafe.Pointer(box)), + (*C.GtkWidget)(unsafe.Pointer(image))) + } + + C.gtk_box_pack_end( + (*C.GtkBox)(unsafe.Pointer(box)), + (*C.GtkWidget)(lbl), 1, 1, 0) + C.gtk_container_add( + (*C.GtkContainer)(unsafe.Pointer(menuItem)), + (*C.GtkWidget)(unsafe.Pointer(box))) + C.gtk_widget_show_all(menuItem) + return pointer(menuItem) +} + +func menuCheckItemNew(label string, bitmap []byte) pointer { + return menuItemAddProperties(C.gtk_check_menu_item_new(), label, bitmap) +} + +func menuItemSetChecked(widget pointer, checked bool) { + value := C.int(0) + if checked { + value = C.int(1) + } + C.gtk_check_menu_item_set_active( + (*C.GtkCheckMenuItem)(widget), + value) +} + +func menuItemSetDisabled(widget pointer, disabled bool) { + value := C.int(1) + if disabled { + value = C.int(0) + } + C.gtk_widget_set_sensitive( + (*C.GtkWidget)(widget), + value) +} + +func menuItemSetLabel(widget pointer, label string) { + value := C.CString(label) + C.gtk_menu_item_set_label( + (*C.GtkMenuItem)(widget), + value) + C.free(unsafe.Pointer(value)) +} + +func menuItemRemoveBitmap(widget pointer) { + box := C.gtk_bin_get_child((*C.GtkBin)(widget)) + if box == nil { + return + } + + children := C.gtk_container_get_children((*C.GtkContainer)(unsafe.Pointer(box))) + defer C.g_list_free(children) + count := int(C.g_list_length(children)) + if count == 2 { + C.gtk_container_remove((*C.GtkContainer)(unsafe.Pointer(box)), + (*C.GtkWidget)(children.data)) + } +} + +func menuItemSetBitmap(widget pointer, bitmap []byte) { + menuItemRemoveBitmap(widget) + box := C.gtk_bin_get_child((*C.GtkBin)(widget)) + if img, err := pngToImage(bitmap); err == nil { + gbytes := C.g_bytes_new_static(C.gconstpointer(unsafe.Pointer(&img.Pix[0])), + C.ulong(len(img.Pix))) + defer C.g_bytes_unref(gbytes) + pixBuf := C.gdk_pixbuf_new_from_bytes( + gbytes, + C.GDK_COLORSPACE_RGB, + 1, // has_alpha + 8, + C.int(img.Bounds().Dx()), + C.int(img.Bounds().Dy()), + C.int(img.Stride), + ) + image := C.gtk_image_new_from_pixbuf(pixBuf) + C.gtk_widget_set_visible((*C.GtkWidget)(image), C.gboolean(1)) + C.gtk_container_add( + (*C.GtkContainer)(unsafe.Pointer(box)), + (*C.GtkWidget)(unsafe.Pointer(image))) + } + +} + +func menuItemSetToolTip(widget pointer, tooltip string) { + value := C.CString(tooltip) + C.gtk_widget_set_tooltip_text( + (*C.GtkWidget)(widget), + value) + C.free(unsafe.Pointer(value)) +} + +func menuItemSignalBlock(widget pointer, handlerId uint, block bool) { + if block { + C.g_signal_handler_block(C.gpointer(widget), C.ulong(handlerId)) + } else { + C.g_signal_handler_unblock(C.gpointer(widget), C.ulong(handlerId)) + } +} + +func menuRadioItemNew(group *GSList, label string) pointer { + cLabel := C.CString(label) + defer C.free(unsafe.Pointer(cLabel)) + return pointer(C.gtk_radio_menu_item_new_with_label((*C.GSList)(group), cLabel)) +} + +// screen related + +func getScreenByIndex(display *C.struct__GdkDisplay, index int) *Screen { + monitor := C.gdk_display_get_monitor(display, C.int(index)) + // TODO: Do we need to update Screen to contain current info? + // currentMonitor := C.gdk_display_get_monitor_at_window(display, window) + + var geometry C.GdkRectangle + C.gdk_monitor_get_geometry(monitor, &geometry) + primary := false + if C.gdk_monitor_is_primary(monitor) == 1 { + primary = true + } + name := C.gdk_monitor_get_model(monitor) + return &Screen{ + ID: fmt.Sprintf("%d", index), + Name: C.GoString(name), + IsPrimary: primary, + ScaleFactor: float32(C.gdk_monitor_get_scale_factor(monitor)), + X: int(geometry.x), + Y: int(geometry.y), + Size: Size{ + Height: int(geometry.height), + Width: int(geometry.width), + }, + Bounds: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + PhysicalBounds: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + WorkArea: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + PhysicalWorkArea: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + Rotation: 0.0, + } +} + +func getScreens(app pointer) ([]*Screen, error) { + var screens []*Screen + window := C.gtk_application_get_active_window((*C.GtkApplication)(app)) + gdkWindow := C.gtk_widget_get_window((*C.GtkWidget)(unsafe.Pointer(window))) + display := C.gdk_window_get_display(gdkWindow) + count := C.gdk_display_get_n_monitors(display) + for i := 0; i < int(count); i++ { + screens = append(screens, getScreenByIndex(display, i)) + } + return screens, nil +} + +// widgets + +func (w *linuxWebviewWindow) setEnabled(enabled bool) { + var value C.int + if enabled { + value = C.int(1) + } + + C.gtk_widget_set_sensitive(w.gtkWidget(), value) +} + +func widgetSetVisible(widget pointer, hidden bool) { + if hidden { + C.gtk_widget_hide((*C.GtkWidget)(widget)) + } else { + C.gtk_widget_show((*C.GtkWidget)(widget)) + } +} + +func (w *linuxWebviewWindow) close() { + C.gtk_widget_destroy(w.gtkWidget()) + getNativeApplication().unregisterWindow(windowPointer(w.window)) +} + +func (w *linuxWebviewWindow) enableDND() { + C.gtk_drag_dest_unset((*C.GtkWidget)(w.webview)) + + windowId := C.uint(w.parent.id) + C.enableDND((*C.GtkWidget)(w.vbox), C.gpointer(&windowId)) +} + +func (w *linuxWebviewWindow) execJS(js string) { + InvokeAsync(func() { + value := C.CString(js) + C.webkit_web_view_evaluate_javascript(w.webKitWebView(), + value, + C.long(len(js)), + nil, + C.CString(""), + nil, + nil, + nil) + C.free(unsafe.Pointer(value)) + }) +} + +func getMousePosition() (int, int, *Screen) { + var x, y C.gint + var screen *C.GdkScreen + defaultDisplay := C.gdk_display_get_default() + device := C.gdk_seat_get_pointer(C.gdk_display_get_default_seat(defaultDisplay)) + C.gdk_device_get_position(device, &screen, &x, &y) + // Get Monitor for screen + monitor := C.gdk_display_get_monitor_at_point(defaultDisplay, x, y) + geometry := C.GdkRectangle{} + C.gdk_monitor_get_geometry(monitor, &geometry) + scaleFactor := int(C.gdk_monitor_get_scale_factor(monitor)) + return int(x), int(y), &Screen{ + ID: fmt.Sprintf("%d", 0), // A unique identifier for the display + Name: C.GoString(C.gdk_monitor_get_model(monitor)), // The name of the display + ScaleFactor: float32(scaleFactor), // The scale factor of the display + X: int(geometry.x), // The x-coordinate of the top-left corner of the rectangle + Y: int(geometry.y), // The y-coordinate of the top-left corner of the rectangle + Size: Size{ + Height: int(geometry.height), + Width: int(geometry.width), + }, + Bounds: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + WorkArea: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + IsPrimary: false, + Rotation: 0.0, + } +} + +func (w *linuxWebviewWindow) destroy() { + w.parent.markAsDestroyed() + // Free menu + if w.gtkmenu != nil { + C.gtk_widget_destroy((*C.GtkWidget)(w.gtkmenu)) + w.gtkmenu = nil + } + // Free window + C.gtk_widget_destroy(w.gtkWidget()) +} + +func (w *linuxWebviewWindow) fullscreen() { + w.maximise() + //w.lastWidth, w.lastHeight = w.size() + x, y, width, height, scaleFactor := w.getCurrentMonitorGeometry() + if x == -1 && y == -1 && width == -1 && height == -1 { + return + } + w.setMinMaxSize(0, 0, width*scaleFactor, height*scaleFactor) + w.setSize(width*scaleFactor, height*scaleFactor) + C.gtk_window_fullscreen(w.gtkWindow()) + w.setRelativePosition(0, 0) +} + +func (w *linuxWebviewWindow) getCurrentMonitor() *C.GdkMonitor { + // Get the monitor that the window is currently on + display := C.gtk_widget_get_display(w.gtkWidget()) + gdkWindow := C.gtk_widget_get_window(w.gtkWidget()) + if gdkWindow == nil { + return nil + } + return C.gdk_display_get_monitor_at_window(display, gdkWindow) +} + +func (w *linuxWebviewWindow) getScreen() (*Screen, error) { + // Get the current screen for the window + monitor := w.getCurrentMonitor() + name := C.gdk_monitor_get_model(monitor) + mx, my, width, height, scaleFactor := w.getCurrentMonitorGeometry() + return &Screen{ + ID: fmt.Sprintf("%d", w.id), // A unique identifier for the display + Name: C.GoString(name), // The name of the display + ScaleFactor: float32(scaleFactor), // The scale factor of the display + X: mx, // The x-coordinate of the top-left corner of the rectangle + Y: my, // The y-coordinate of the top-left corner of the rectangle + Size: Size{ + Height: int(height), + Width: int(width), + }, + Bounds: Rect{ + X: int(mx), + Y: int(my), + Height: int(height), + Width: int(width), + }, + WorkArea: Rect{ + X: int(mx), + Y: int(my), + Height: int(height), + Width: int(width), + }, + PhysicalBounds: Rect{ + X: int(mx), + Y: int(my), + Height: int(height), + Width: int(width), + }, + PhysicalWorkArea: Rect{ + X: int(mx), + Y: int(my), + Height: int(height), + Width: int(width), + }, + IsPrimary: false, + Rotation: 0.0, + }, nil +} + +func (w *linuxWebviewWindow) getCurrentMonitorGeometry() (x int, y int, width int, height int, scaleFactor int) { + monitor := w.getCurrentMonitor() + if monitor == nil { + // Best effort to find screen resolution of default monitor + display := C.gdk_display_get_default() + monitor = C.gdk_display_get_primary_monitor(display) + if monitor == nil { + return -1, -1, -1, -1, 1 + } + } + var result C.GdkRectangle + C.gdk_monitor_get_geometry(monitor, &result) + scaleFactor = int(C.gdk_monitor_get_scale_factor(monitor)) + return int(result.x), int(result.y), int(result.width), int(result.height), scaleFactor +} + +func (w *linuxWebviewWindow) size() (int, int) { + var windowWidth C.int + var windowHeight C.int + C.gtk_window_get_size(w.gtkWindow(), &windowWidth, &windowHeight) + return int(windowWidth), int(windowHeight) +} + +func (w *linuxWebviewWindow) relativePosition() (int, int) { + x, y := w.position() + // The position must be relative to the screen it is on + // We need to get the screen it is on + monitor := w.getCurrentMonitor() + geometry := C.GdkRectangle{} + C.gdk_monitor_get_geometry(monitor, &geometry) + x = x - int(geometry.x) + y = y - int(geometry.y) + + // TODO: Scale based on DPI + + return x, y +} + +func (w *linuxWebviewWindow) gtkWidget() *C.GtkWidget { + return (*C.GtkWidget)(w.window) +} + +func (w *linuxWebviewWindow) windowHide() { + C.gtk_widget_hide(w.gtkWidget()) +} + +func (w *linuxWebviewWindow) isFullscreen() bool { + gdkWindow := C.gtk_widget_get_window(w.gtkWidget()) + state := C.gdk_window_get_state(gdkWindow) + return state&C.GDK_WINDOW_STATE_FULLSCREEN > 0 +} + +func (w *linuxWebviewWindow) isFocused() bool { + // returns true if window is focused + return C.gtk_window_has_toplevel_focus(w.gtkWindow()) == 1 +} + +func (w *linuxWebviewWindow) isMaximised() bool { + gdkwindow := C.gtk_widget_get_window(w.gtkWidget()) + state := C.gdk_window_get_state(gdkwindow) + return state&C.GDK_WINDOW_STATE_MAXIMIZED > 0 && state&C.GDK_WINDOW_STATE_FULLSCREEN == 0 +} + +func (w *linuxWebviewWindow) isMinimised() bool { + gdkwindow := C.gtk_widget_get_window(w.gtkWidget()) + state := C.gdk_window_get_state(gdkwindow) + return state&C.GDK_WINDOW_STATE_ICONIFIED > 0 +} + +func (w *linuxWebviewWindow) isVisible() bool { + if C.gtk_widget_is_visible(w.gtkWidget()) == 1 { + return true + } + return false +} + +func (w *linuxWebviewWindow) maximise() { + C.gtk_window_maximize(w.gtkWindow()) +} + +func (w *linuxWebviewWindow) minimise() { + C.gtk_window_iconify(w.gtkWindow()) +} + +func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy WebviewGpuPolicy) (window, webview, vbox pointer) { + window = pointer(C.gtk_application_window_new((*C.GtkApplication)(application))) + C.g_object_ref_sink(C.gpointer(window)) + webview = windowNewWebview(windowId, gpuPolicy) + vbox = pointer(C.gtk_box_new(C.GTK_ORIENTATION_VERTICAL, 0)) + name := C.CString("webview-box") + defer C.free(unsafe.Pointer(name)) + C.gtk_widget_set_name((*C.GtkWidget)(vbox), name) + + C.gtk_container_add((*C.GtkContainer)(window), (*C.GtkWidget)(vbox)) + if menu != nil { + C.gtk_box_pack_start((*C.GtkBox)(vbox), (*C.GtkWidget)(menu), 0, 0, 0) + } + C.gtk_box_pack_start((*C.GtkBox)(unsafe.Pointer(vbox)), (*C.GtkWidget)(webview), 1, 1, 0) + return +} + +func windowNewWebview(parentId uint, gpuPolicy WebviewGpuPolicy) pointer { + c := NewCalloc() + defer c.Free() + manager := C.webkit_user_content_manager_new() + C.webkit_user_content_manager_register_script_message_handler(manager, c.String("external")) + webView := C.webkit_web_view_new_with_user_content_manager(manager) + + // attach window id to both the webview and contentmanager + C.save_window_id(unsafe.Pointer(webView), C.uint(parentId)) + C.save_window_id(unsafe.Pointer(manager), C.uint(parentId)) + + registerURIScheme.Do(func() { + context := C.webkit_web_view_get_context(C.webkit_web_view(webView)) + C.webkit_web_context_register_uri_scheme( + context, + c.String("wails"), + C.WebKitURISchemeRequestCallback(C.onProcessRequest), + nil, + nil) + }) + settings := C.webkit_web_view_get_settings((*C.WebKitWebView)(unsafe.Pointer(webView))) + C.webkit_settings_set_user_agent_with_application_details(settings, c.String("wails.io"), c.String("")) + + switch gpuPolicy { + case WebviewGpuPolicyAlways: + C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS) + break + case WebviewGpuPolicyOnDemand: + C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND) + break + case WebviewGpuPolicyNever: + C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER) + break + default: + C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND) + } + return pointer(webView) +} + +func (w *linuxWebviewWindow) present() { + C.gtk_window_present(w.gtkWindow()) + // gtk_window_unminimize (w.gtkWindow()) /// gtk4 +} + +func (w *linuxWebviewWindow) setSize(width, height int) { + C.gtk_window_resize( + w.gtkWindow(), + C.gint(width), + C.gint(height)) +} + +func (w *linuxWebviewWindow) windowShow() { + if w.gtkWidget() == nil { + return + } + C.gtk_widget_show_all(w.gtkWidget()) +} + +func windowIgnoreMouseEvents(window pointer, webview pointer, ignore bool) { + var enable C.int + if ignore { + enable = 1 + } + gdkWindow := (*C.GdkWindow)(window) + C.gdk_window_set_pass_through(gdkWindow, enable) + C.webkit_web_view_set_editable((*C.WebKitWebView)(webview), C.gboolean(enable)) +} + +func (w *linuxWebviewWindow) webKitWebView() *C.WebKitWebView { + return (*C.WebKitWebView)(w.webview) +} + +func (w *linuxWebviewWindow) setBorderless(borderless bool) { + C.gtk_window_set_decorated(w.gtkWindow(), gtkBool(!borderless)) +} + +func (w *linuxWebviewWindow) setResizable(resizable bool) { + C.gtk_window_set_resizable(w.gtkWindow(), gtkBool(resizable)) +} + +func (w *linuxWebviewWindow) setDefaultSize(width int, height int) { + C.gtk_window_set_default_size(w.gtkWindow(), C.gint(width), C.gint(height)) +} + +func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) { + rgba := C.GdkRGBA{C.double(colour.Red) / 255.0, C.double(colour.Green) / 255.0, C.double(colour.Blue) / 255.0, C.double(colour.Alpha) / 255.0} + C.webkit_web_view_set_background_color((*C.WebKitWebView)(w.webview), &rgba) + + colour.Alpha = 255 + cssStr := C.CString(fmt.Sprintf("#webview-box {background-color: rgba(%d, %d, %d, %1.1f);}", colour.Red, colour.Green, colour.Blue, float32(colour.Alpha)/255.0)) + provider := C.gtk_css_provider_new() + C.gtk_style_context_add_provider( + C.gtk_widget_get_style_context((*C.GtkWidget)(w.vbox)), + (*C.GtkStyleProvider)(unsafe.Pointer(provider)), + C.GTK_STYLE_PROVIDER_PRIORITY_USER) + C.g_object_unref(C.gpointer(provider)) + C.gtk_css_provider_load_from_data(provider, cssStr, -1, nil) + C.free(unsafe.Pointer(cssStr)) +} + +func getPrimaryScreen() (*Screen, error) { + display := C.gdk_display_get_default() + monitor := C.gdk_display_get_primary_monitor(display) + geometry := C.GdkRectangle{} + C.gdk_monitor_get_geometry(monitor, &geometry) + scaleFactor := int(C.gdk_monitor_get_scale_factor(monitor)) + // get the name for the screen + name := C.gdk_monitor_get_model(monitor) + return &Screen{ + ID: "0", + Name: C.GoString(name), + IsPrimary: true, + X: int(geometry.x), + Y: int(geometry.y), + Size: Size{ + Height: int(geometry.height), + Width: int(geometry.width), + }, + Bounds: Rect{ + X: int(geometry.x), + Y: int(geometry.y), + Height: int(geometry.height), + Width: int(geometry.width), + }, + ScaleFactor: float32(scaleFactor), + }, nil +} + +func windowSetGeometryHints(window pointer, minWidth, minHeight, maxWidth, maxHeight int) { + size := C.GdkGeometry{ + min_width: C.int(minWidth), + min_height: C.int(minHeight), + max_width: C.int(maxWidth), + max_height: C.int(maxHeight), + } + C.gtk_window_set_geometry_hints((*C.GtkWindow)(window), nil, &size, C.GDK_HINT_MAX_SIZE|C.GDK_HINT_MIN_SIZE) +} + +func (w *linuxWebviewWindow) setFrameless(frameless bool) { + C.gtk_window_set_decorated(w.gtkWindow(), gtkBool(!frameless)) + // TODO: Deal with transparency for the titlebar if possible when !frameless + // Perhaps we just make it undecorated and add a menu bar inside? +} + +// TODO: confirm this is working properly +func (w *linuxWebviewWindow) setHTML(html string) { + cHTML := C.CString(html) + uri := C.CString("wails://") + empty := C.CString("") + defer C.free(unsafe.Pointer(cHTML)) + defer C.free(unsafe.Pointer(uri)) + defer C.free(unsafe.Pointer(empty)) + C.webkit_web_view_load_alternate_html( + w.webKitWebView(), + cHTML, + uri, + empty) +} + +func (w *linuxWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { + C.gtk_window_set_keep_above(w.gtkWindow(), gtkBool(alwaysOnTop)) +} + +func (w *linuxWebviewWindow) flash(_ bool) { + // Not supported on Linux +} + +func (w *linuxWebviewWindow) setTitle(title string) { + if !w.parent.options.Frameless { + cTitle := C.CString(title) + C.gtk_window_set_title(w.gtkWindow(), cTitle) + C.free(unsafe.Pointer(cTitle)) + } +} + +func (w *linuxWebviewWindow) setIcon(icon pointer) { + if icon != nil { + C.gtk_window_set_icon(w.gtkWindow(), (*C.GdkPixbuf)(icon)) + } +} + +func (w *linuxWebviewWindow) gtkWindow() *C.GtkWindow { + return (*C.GtkWindow)(w.window) +} + +func (w *linuxWebviewWindow) setTransparent() { + screen := C.gtk_widget_get_screen(w.gtkWidget()) + visual := C.gdk_screen_get_rgba_visual(screen) + + if visual != nil && C.gdk_screen_is_composited(screen) == C.int(1) { + C.gtk_widget_set_app_paintable(w.gtkWidget(), C.gboolean(1)) + C.gtk_widget_set_visual(w.gtkWidget(), visual) + } +} + +func (w *linuxWebviewWindow) setURL(uri string) { + target := C.CString(uri) + C.webkit_web_view_load_uri(w.webKitWebView(), target) + C.free(unsafe.Pointer(target)) +} + +//export emit +func emit(we *C.WindowEvent) { + window, _ := globalApplication.Window.GetByID(uint(we.id)) + if window != nil { + windowEvents <- &windowEvent{ + WindowID: window.ID(), + EventID: uint(events.WindowEventType(we.event)), + } + } +} + +//export handleConfigureEvent +func handleConfigureEvent(widget *C.GtkWidget, event *C.GdkEventConfigure, data C.uintptr_t) C.gboolean { + window, _ := globalApplication.Window.GetByID(uint(data)) + if window != nil { + lw := getLinuxWebviewWindow(window) + if lw == nil { + return C.gboolean(1) + } + if lw.lastX != int(event.x) || lw.lastY != int(event.y) { + lw.moveDebouncer(func() { + processWindowEvent(C.uint(data), C.uint(events.Linux.WindowDidMove)) + }) + } + + if lw.lastWidth != int(event.width) || lw.lastHeight != int(event.height) { + lw.resizeDebouncer(func() { + processWindowEvent(C.uint(data), C.uint(events.Linux.WindowDidResize)) + }) + } + + lw.lastX = int(event.x) + lw.lastY = int(event.y) + lw.lastWidth = int(event.width) + lw.lastHeight = int(event.height) + } + + return C.gboolean(0) +} + +//export handleDeleteEvent +func handleDeleteEvent(widget *C.GtkWidget, event *C.GdkEvent, data C.uintptr_t) C.gboolean { + processWindowEvent(C.uint(data), C.uint(events.Linux.WindowDeleteEvent)) + return C.gboolean(1) +} + +//export handleFocusEvent +func handleFocusEvent(widget *C.GtkWidget, event *C.GdkEvent, data C.uintptr_t) C.gboolean { + focusEvent := (*C.GdkEventFocus)(unsafe.Pointer(event)) + if focusEvent._type == C.GDK_FOCUS_CHANGE { + if focusEvent.in == C.TRUE { + processWindowEvent(C.uint(data), C.uint(events.Linux.WindowFocusIn)) + } else { + processWindowEvent(C.uint(data), C.uint(events.Linux.WindowFocusOut)) + } + } + return C.gboolean(0) +} + +//export handleLoadChanged +func handleLoadChanged(webview *C.WebKitWebView, event C.WebKitLoadEvent, data C.uintptr_t) { + switch event { + case C.WEBKIT_LOAD_FINISHED: + processWindowEvent(C.uint(data), C.uint(events.Linux.WindowLoadChanged)) + } +} + +func (w *linuxWebviewWindow) setupSignalHandlers(emit func(e events.WindowEventType)) { + + c := NewCalloc() + defer c.Free() + + winID := unsafe.Pointer(uintptr(C.uint(w.parent.ID()))) + + // Set up the window close event + wv := unsafe.Pointer(w.webview) + C.signal_connect(unsafe.Pointer(w.window), c.String("delete-event"), C.handleDeleteEvent, winID) + C.signal_connect(unsafe.Pointer(w.window), c.String("focus-out-event"), C.handleFocusEvent, winID) + C.signal_connect(wv, c.String("load-changed"), C.handleLoadChanged, winID) + C.signal_connect(unsafe.Pointer(w.window), c.String("configure-event"), C.handleConfigureEvent, winID) + + contentManager := C.webkit_web_view_get_user_content_manager(w.webKitWebView()) + C.signal_connect(unsafe.Pointer(contentManager), c.String("script-message-received::external"), C.sendMessageToBackend, nil) + C.signal_connect(wv, c.String("button-press-event"), C.onButtonEvent, winID) + C.signal_connect(wv, c.String("button-release-event"), C.onButtonEvent, winID) + C.signal_connect(wv, c.String("key-press-event"), C.onKeyPressEvent, winID) +} + +func getMouseButtons() (bool, bool, bool) { + var pointer *C.GdkDevice + var state C.GdkModifierType + pointer = C.gdk_seat_get_pointer(C.gdk_display_get_default_seat(C.gdk_display_get_default())) + C.gdk_device_get_state(pointer, nil, nil, &state) + return state&C.GDK_BUTTON1_MASK > 0, state&C.GDK_BUTTON2_MASK > 0, state&C.GDK_BUTTON3_MASK > 0 +} + +func openDevTools(webview pointer) { + inspector := C.webkit_web_view_get_inspector((*C.WebKitWebView)(webview)) + C.webkit_web_inspector_show(inspector) +} + +func (w *linuxWebviewWindow) startDrag() error { + C.gtk_window_begin_move_drag( + (*C.GtkWindow)(w.window), + C.int(w.drag.MouseButton), + C.int(w.drag.XRoot), + C.int(w.drag.YRoot), + C.uint32_t(w.drag.DragTime)) + return nil +} + +func enableDevTools(webview pointer) { + settings := C.webkit_web_view_get_settings((*C.WebKitWebView)(webview)) + enabled := C.webkit_settings_get_enable_developer_extras(settings) + switch enabled { + case C.int(0): + enabled = C.int(1) + case C.int(1): + enabled = C.int(0) + } + C.webkit_settings_set_enable_developer_extras(settings, enabled) +} + +func (w *linuxWebviewWindow) unfullscreen() { + C.gtk_window_unfullscreen((*C.GtkWindow)(w.window)) + w.unmaximise() +} + +func (w *linuxWebviewWindow) unmaximise() { + C.gtk_window_unmaximize((*C.GtkWindow)(w.window)) +} + +func (w *linuxWebviewWindow) getZoom() float64 { + return float64(C.webkit_web_view_get_zoom_level(w.webKitWebView())) +} + +func (w *linuxWebviewWindow) zoomIn() { + // FIXME: ZoomIn/Out is assumed to be incorrect! + ZoomInFactor := 1.10 + w.setZoom(w.getZoom() * ZoomInFactor) +} + +func (w *linuxWebviewWindow) zoomOut() { + ZoomInFactor := -1.10 + w.setZoom(w.getZoom() * ZoomInFactor) +} + +func (w *linuxWebviewWindow) zoomReset() { + w.setZoom(1.0) +} + +func (w *linuxWebviewWindow) reload() { + uri := C.CString("wails://") + C.webkit_web_view_load_uri(w.webKitWebView(), uri) + C.free(unsafe.Pointer(uri)) +} + +func (w *linuxWebviewWindow) setZoom(zoom float64) { + if zoom < 1 { // 1.0 is the smallest allowable + zoom = 1 + } + C.webkit_web_view_set_zoom_level(w.webKitWebView(), C.double(zoom)) +} + +func (w *linuxWebviewWindow) move(x, y int) { + // Move the window to these coordinates + C.gtk_window_move(w.gtkWindow(), C.int(x), C.int(y)) +} + +func (w *linuxWebviewWindow) position() (int, int) { + var x C.int + var y C.int + C.gtk_window_get_position((*C.GtkWindow)(w.window), &x, &y) + return int(x), int(y) +} + +func (w *linuxWebviewWindow) ignoreMouse(ignore bool) { + if ignore { + C.gtk_widget_set_events((*C.GtkWidget)(unsafe.Pointer(w.window)), C.GDK_ENTER_NOTIFY_MASK|C.GDK_LEAVE_NOTIFY_MASK) + } else { + C.gtk_widget_set_events((*C.GtkWidget)(unsafe.Pointer(w.window)), C.GDK_ALL_EVENTS_MASK) + } +} + +// FIXME Change this to reflect mouse button! +// +//export onButtonEvent +func onButtonEvent(_ *C.GtkWidget, event *C.GdkEventButton, data C.uintptr_t) C.gboolean { + // Constants (defined here to be easier to use with purego) + GdkButtonPress := C.GDK_BUTTON_PRESS // 4 + Gdk2ButtonPress := C.GDK_2BUTTON_PRESS // 5 for double-click + GdkButtonRelease := C.GDK_BUTTON_RELEASE // 7 + + windowId := uint(C.uint(data)) + window, _ := globalApplication.Window.GetByID(windowId) + if window == nil { + return C.gboolean(0) + } + lw := getLinuxWebviewWindow(window) + if lw == nil { + return C.gboolean(0) + } + + if event == nil { + return C.gboolean(0) + } + if event.button == 3 { + return C.gboolean(0) + } + + switch int(event._type) { + case GdkButtonPress: + lw.drag.MouseButton = uint(event.button) + lw.drag.XRoot = int(event.x_root) + lw.drag.YRoot = int(event.y_root) + lw.drag.DragTime = uint32(event.time) + case Gdk2ButtonPress: + // do we need something here? + case GdkButtonRelease: + lw.endDrag(uint(event.button), int(event.x_root), int(event.y_root)) + } + + return C.gboolean(0) +} + +//export onMenuButtonEvent +func onMenuButtonEvent(_ *C.GtkWidget, event *C.GdkEventButton, data C.uintptr_t) C.gboolean { + // Constants (defined here to be easier to use with purego) + GdkButtonRelease := C.GDK_BUTTON_RELEASE // 7 + + windowId := uint(C.uint(data)) + window, _ := globalApplication.Window.GetByID(windowId) + if window == nil { + return C.gboolean(0) + } + lw := getLinuxWebviewWindow(window) + if lw == nil { + return C.gboolean(0) + } + + // prevent custom context menu from closing immediately + if event.button == 3 && int(event._type) == GdkButtonRelease && lw.ctxMenuOpened { + lw.ctxMenuOpened = false + return C.gboolean(1) + } + + return C.gboolean(0) +} + +//export onUriList +func onUriList(extracted **C.char, x C.gint, y C.gint, data unsafe.Pointer) { + // Credit: https://groups.google.com/g/golang-nuts/c/bI17Bpck8K4/m/DVDa7EMtDAAJ + offset := unsafe.Sizeof(uintptr(0)) + filenames := []string{} + for *extracted != nil { + filenames = append(filenames, strings.TrimPrefix(C.GoString(*extracted), "file://")) + extracted = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(extracted)) + offset)) + } + + windowDragAndDropBuffer <- &dragAndDropMessage{ + windowId: uint(*((*C.uint)(data))), + filenames: filenames, + X: int(x), + Y: int(y), + } +} + +var debounceTimer *time.Timer +var isDebouncing bool = false + +//export onKeyPressEvent +func onKeyPressEvent(_ *C.GtkWidget, event *C.GdkEventKey, userData C.uintptr_t) C.gboolean { + // Keypress re-emits if the key is pressed over a certain threshold so we need a debounce + if isDebouncing { + debounceTimer.Reset(50 * time.Millisecond) + return C.gboolean(0) + } + + // Start the debounce + isDebouncing = true + debounceTimer = time.AfterFunc(50*time.Millisecond, func() { + isDebouncing = false + }) + + windowID := uint(C.uint(userData)) + if accelerator, ok := getKeyboardState(event); ok { + windowKeyEvents <- &windowKeyEvent{ + windowId: windowID, + acceleratorString: accelerator, + } + } + return C.gboolean(0) +} + +func getKeyboardState(event *C.GdkEventKey) (string, bool) { + modifiers := uint(event.state) & C.GDK_MODIFIER_MASK + keyCode := uint(event.keyval) + + var acc accelerator + // Check Accelerators + if modifiers&(C.GDK_SHIFT_MASK) != 0 { + acc.Modifiers = append(acc.Modifiers, ShiftKey) + } + if modifiers&(C.GDK_CONTROL_MASK) != 0 { + acc.Modifiers = append(acc.Modifiers, ControlKey) + } + if modifiers&(C.GDK_MOD1_MASK) != 0 { + acc.Modifiers = append(acc.Modifiers, OptionOrAltKey) + } + if modifiers&(C.GDK_SUPER_MASK) != 0 { + acc.Modifiers = append(acc.Modifiers, SuperKey) + } + keyString, ok := VirtualKeyCodes[keyCode] + if !ok { + return "", false + } + acc.Key = keyString + return acc.String(), true +} + +//export onProcessRequest +func onProcessRequest(request *C.WebKitURISchemeRequest, data C.uintptr_t) { + webView := C.webkit_uri_scheme_request_get_web_view(request) + windowId := uint(C.get_window_id(unsafe.Pointer(webView))) + webviewRequests <- &webViewAssetRequest{ + Request: webview.NewRequest(unsafe.Pointer(request)), + windowId: windowId, + windowName: func() string { + if window, ok := globalApplication.Window.GetByID(windowId); ok { + return window.Name() + } + return "" + }(), + } +} + +//export sendMessageToBackend +func sendMessageToBackend(contentManager *C.WebKitUserContentManager, result *C.WebKitJavascriptResult, + data unsafe.Pointer) { + + // Get the windowID from the contentManager + thisWindowID := uint(C.get_window_id(unsafe.Pointer(contentManager))) + + var msg string + value := C.webkit_javascript_result_get_js_value(result) + message := C.jsc_value_to_string(value) + msg = C.GoString(message) + defer C.g_free(C.gpointer(message)) + windowMessageBuffer <- &windowMessage{ + windowId: thisWindowID, + message: msg, + } +} + +func gtkBool(input bool) C.gboolean { + if input { + return C.gboolean(1) + } + return C.gboolean(0) +} + +// dialog related + +func setWindowIcon(window pointer, icon []byte) { + loader := C.gdk_pixbuf_loader_new() + if loader == nil { + return + } + written := C.gdk_pixbuf_loader_write( + loader, + (*C.uchar)(&icon[0]), + C.ulong(len(icon)), + nil) + if written == 0 { + return + } + C.gdk_pixbuf_loader_close(loader, nil) + pixbuf := C.gdk_pixbuf_loader_get_pixbuf(loader) + if pixbuf != nil { + C.gtk_window_set_icon((*C.GtkWindow)(window), pixbuf) + } + C.g_object_unref(C.gpointer(loader)) +} + +//export messageDialogCB +func messageDialogCB(button C.int) { + fmt.Println("messageDialogCB", button) +} + +func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden bool, currentFolder, title string, action int, acceptLabel string, filters []FileFilter) (chan string, error) { + titleStr := C.CString(title) + defer C.free(unsafe.Pointer(titleStr)) + cancelStr := C.CString("_Cancel") + defer C.free(unsafe.Pointer(cancelStr)) + acceptLabelStr := C.CString(acceptLabel) + defer C.free(unsafe.Pointer(acceptLabelStr)) + + fc := C.gtkFileChooserDialogNew( + titleStr, + (*C.GtkWindow)(window), + C.GtkFileChooserAction(action), + cancelStr, + acceptLabelStr) + + C.gtk_file_chooser_set_action((*C.GtkFileChooser)(fc), C.GtkFileChooserAction(action)) + + gtkFilters := []*C.GtkFileFilter{} + for _, filter := range filters { + f := C.gtk_file_filter_new() + displayStr := C.CString(filter.DisplayName) + C.gtk_file_filter_set_name(f, displayStr) + C.free(unsafe.Pointer(displayStr)) + patterns := strings.Split(filter.Pattern, ";") + for _, pattern := range patterns { + patternStr := C.CString(strings.TrimSpace(pattern)) + C.gtk_file_filter_add_pattern(f, patternStr) + C.free(unsafe.Pointer(patternStr)) + } + C.gtk_file_chooser_add_filter((*C.GtkFileChooser)(fc), f) + gtkFilters = append(gtkFilters, f) + } + C.gtk_file_chooser_set_select_multiple( + (*C.GtkFileChooser)(fc), + gtkBool(allowMultiple)) + C.gtk_file_chooser_set_create_folders( + (*C.GtkFileChooser)(fc), + gtkBool(createFolders)) + C.gtk_file_chooser_set_show_hidden( + (*C.GtkFileChooser)(fc), + gtkBool(showHidden)) + + if currentFolder != "" { + path := C.CString(currentFolder) + C.gtk_file_chooser_set_current_folder( + (*C.GtkFileChooser)(fc), + path) + C.free(unsafe.Pointer(path)) + } + + // FIXME: This should be consolidated - duplicate exists in linux_purego.go + buildStringAndFree := func(s C.gpointer) string { + bytes := []byte{} + p := unsafe.Pointer(s) + for { + val := *(*byte)(p) + if val == 0 { // this is the null terminator + break + } + bytes = append(bytes, val) + p = unsafe.Add(p, 1) + } + C.g_free(s) // so we don't have to iterate a second time + return string(bytes) + } + + selections := make(chan string) + // run this on the gtk thread + InvokeAsync(func() { + response := C.gtk_dialog_run((*C.GtkDialog)(fc)) + go func() { + defer handlePanic() + if response == C.GTK_RESPONSE_ACCEPT { + filenames := C.gtk_file_chooser_get_filenames((*C.GtkFileChooser)(fc)) + iter := filenames + count := 0 + for { + selections <- buildStringAndFree(C.gpointer(iter.data)) + iter = iter.next + if iter == nil || count == 1024 { + break + } + count++ + } + } + close(selections) + }() + }) + C.gtk_widget_destroy((*C.GtkWidget)(unsafe.Pointer(fc))) + return selections, nil +} + +func runOpenFileDialog(dialog *OpenFileDialogStruct) (chan string, error) { + var action int + + if dialog.canChooseDirectories { + action = C.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + } else { + action = C.GTK_FILE_CHOOSER_ACTION_OPEN + } + + window := nilPointer + if dialog.window != nil { + nativeWindow := dialog.window.NativeWindow() + if nativeWindow != nil { + window = pointer(nativeWindow) + } + } + + buttonText := dialog.buttonText + if buttonText == "" { + buttonText = "_Open" + } + + return runChooserDialog( + window, + dialog.allowsMultipleSelection, + dialog.canCreateDirectories, + dialog.showHiddenFiles, + dialog.directory, + dialog.title, + action, + buttonText, + dialog.filters) +} + +func runQuestionDialog(parent pointer, options *MessageDialog) int { + cMsg := C.CString(options.Message) + cTitle := C.CString(options.Title) + defer C.free(unsafe.Pointer(cMsg)) + defer C.free(unsafe.Pointer(cTitle)) + hasButtons := false + if len(options.Buttons) > 0 { + hasButtons = true + } + + dType, ok := map[DialogType]C.int{ + InfoDialogType: C.GTK_MESSAGE_INFO, + // ErrorDialogType: + QuestionDialogType: C.GTK_MESSAGE_QUESTION, + WarningDialogType: C.GTK_MESSAGE_WARNING, + }[options.DialogType] + if !ok { + // FIXME: Add logging here! + dType = C.GTK_MESSAGE_INFO + } + + dialog := C.new_message_dialog((*C.GtkWindow)(parent), cMsg, dType, C.bool(hasButtons)) + if options.Title != "" { + C.gtk_window_set_title( + (*C.GtkWindow)(unsafe.Pointer(dialog)), + cTitle) + } + + if img, err := pngToImage(options.Icon); err == nil { + gbytes := C.g_bytes_new_static( + C.gconstpointer(unsafe.Pointer(&img.Pix[0])), + C.ulong(len(img.Pix))) + defer C.g_bytes_unref(gbytes) + pixBuf := C.gdk_pixbuf_new_from_bytes( + gbytes, + C.GDK_COLORSPACE_RGB, + 1, // has_alpha + 8, + C.int(img.Bounds().Dx()), + C.int(img.Bounds().Dy()), + C.int(img.Stride), + ) + image := C.gtk_image_new_from_pixbuf(pixBuf) + C.gtk_widget_set_visible((*C.GtkWidget)(image), C.gboolean(1)) + contentArea := C.gtk_dialog_get_content_area((*C.GtkDialog)(dialog)) + C.gtk_container_add( + (*C.GtkContainer)(unsafe.Pointer(contentArea)), + (*C.GtkWidget)(image)) + } + for i, button := range options.Buttons { + cLabel := C.CString(button.Label) + defer C.free(unsafe.Pointer(cLabel)) + index := C.int(i) + C.gtk_dialog_add_button( + (*C.GtkDialog)(dialog), cLabel, index) + if button.IsDefault { + C.gtk_dialog_set_default_response((*C.GtkDialog)(dialog), index) + } + } + + defer C.gtk_widget_destroy((*C.GtkWidget)(dialog)) + return int(C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(dialog)))) +} + +func runSaveFileDialog(dialog *SaveFileDialogStruct) (chan string, error) { + window := nilPointer + buttonText := dialog.buttonText + if buttonText == "" { + buttonText = "_Save" + } + results, err := runChooserDialog( + window, + false, // multiple selection + dialog.canCreateDirectories, + dialog.showHiddenFiles, + dialog.directory, + dialog.title, + C.GTK_FILE_CHOOSER_ACTION_SAVE, + buttonText, + dialog.filters) + + return results, err +} + +func (w *linuxWebviewWindow) cut() { + //C.webkit_web_view_execute_editing_command(w.webview, C.WEBKIT_EDITING_COMMAND_CUT) +} + +func (w *linuxWebviewWindow) paste() { + //C.webkit_web_view_execute_editing_command(w.webview, C.WEBKIT_EDITING_COMMAND_PASTE) +} + +func (w *linuxWebviewWindow) copy() { + //C.webkit_web_view_execute_editing_command(w.webview, C.WEBKIT_EDITING_COMMAND_COPY) +} + +func (w *linuxWebviewWindow) selectAll() { + //C.webkit_web_view_execute_editing_command(w.webview, C.WEBKIT_EDITING_COMMAND_SELECT_ALL) +} + +func (w *linuxWebviewWindow) undo() { + //C.webkit_web_view_execute_editing_command(w.webview, C.WEBKIT_EDITING_COMMAND_UNDO) +} + +func (w *linuxWebviewWindow) redo() { +} + +func (w *linuxWebviewWindow) delete() { +} diff --git a/v3/pkg/application/linux_purego.go b/v3/pkg/application/linux_purego.go new file mode 100644 index 000000000..f5edf2e59 --- /dev/null +++ b/v3/pkg/application/linux_purego.go @@ -0,0 +1,1231 @@ +//go:build linux && purego + +package application + +import ( + "fmt" + "os" + "strings" + "unsafe" + + "github.com/ebitengine/purego" + "github.com/wailsapp/wails/v3/internal/assetserver/webview" + "github.com/wailsapp/wails/v3/pkg/events" +) + +type windowPointer uintptr +type identifier uint +type pointer uintptr + +// type GSList uintptr +type GSList struct { + data pointer + next *GSList +} + +type GSListPointer *GSList + +const ( + nilPointer pointer = 0 +) + +const ( + GSourceRemove int = 0 + + // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkwindow.h#L121 + GdkHintMinSize = 1 << 1 + GdkHintMaxSize = 1 << 2 + // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkevents.h#L512 + GdkWindowStateIconified = 1 << 1 + GdkWindowStateMaximized = 1 << 2 + GdkWindowStateFullscreen = 1 << 4 + + // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/gtkmessagedialog.h#L87 + GtkButtonsNone int = 0 + GtkButtonsOk = 1 + GtkButtonsClose = 2 + GtkButtonsCancel = 3 + GtkButtonsYesNo = 4 + GtkButtonsOkCancel = 5 + + // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/gtkdialog.h#L36 + GtkDialogModal = 1 << 0 + GtkDialogDestroyWithParent = 1 << 1 + GtkDialogUseHeaderBar = 1 << 2 // actions in header bar instead of action area + + GtkOrientationVertical = 1 + + // enum GtkMessageType + GtkMessageInfo = 0 + GtkMessageWarning = 1 + GtkMessageQuestion = 2 + GtkMessageError = 3 +) + +type GdkGeometry struct { + minWidth int32 + minHeight int32 + maxWidth int32 + maxHeight int32 + baseWidth int32 + baseHeight int32 + widthInc int32 + heightInc int32 + padding int32 + minAspect float64 + maxAspect float64 + GdkGravity int32 +} + +var ( + nilRadioGroup GSListPointer = nil + gtkSignalHandlers map[pointer]uint = map[pointer]uint{} + gtkSignalToMenuItem map[pointer]*MenuItem = map[pointer]*MenuItem{} + mainThreadId uint64 +) + +const ( + // TODO: map distro => so filename - with fallback? + gtk3 = "libgtk-3.so.0" + gtk4 = "libgtk-4.so.1" + webkit4 = "libwebkit2gtk-4.1.so.0" +) + +var ( + gtk uintptr + gtkVersion int + webkit uintptr + + // function references + gApplicationHold func(pointer) + gApplicationQuit func(pointer) + gApplicationName func() string + gApplicationRelease func(pointer) + gApplicationRun func(pointer, int, []string) int + gBytesNewStatic func(uintptr, int) uintptr + gBytesUnref func(uintptr) + gFree func(pointer) + gIdleAdd func(uintptr) + gObjectRefSink func(pointer) + gObjectUnref func(pointer) + gSignalConnectData func(pointer, string, uintptr, pointer, bool, int) int + gSignalConnectObject func(pointer, string, pointer, pointer, int) uint + gSignalHandlerBlock func(pointer, uint) + gSignalHandlerUnblock func(pointer, uint) + gThreadSelf func() uint64 + + // gdk functions + gdkDisplayGetMonitor func(pointer, int) pointer + gdkDisplayGetMonitorAtWindow func(pointer, pointer) pointer + gdkDisplayGetNMonitors func(pointer) int + gdkMonitorGetGeometry func(pointer, pointer) pointer + gdkMonitorGetScaleFactor func(pointer) int + gdkMonitorIsPrimary func(pointer) int + gdkPixbufNewFromBytes func(uintptr, int, int, int, int, int, int) pointer + gdkRgbaParse func(pointer, string) bool + gdkScreenGetRgbaVisual func(pointer) pointer + gdkScreenIsComposited func(pointer) int + gdkWindowGetState func(pointer) int + gdkWindowGetDisplay func(pointer) pointer + + // gtk functions + gtkApplicationNew func(string, uint) pointer + gtkApplicationGetActiveWindow func(pointer) pointer + gtkApplicationGetWindows func(pointer) *GList + gtkApplicationWindowNew func(pointer) pointer + gtkBoxNew func(int, int) pointer + gtkBoxPackStart func(pointer, pointer, int, int, int) + gtkCheckMenuItemGetActive func(pointer) int + gtkCheckMenuItemNewWithLabel func(string) pointer + gtkCheckMenuItemSetActive func(pointer, int) + gtkContainerAdd func(pointer, pointer) + gtkCSSProviderLoadFromData func(pointer, string, int, pointer) + gtkCSSProviderNew func() pointer + gtkDialogAddButton func(pointer, string, int) + gtkDialogGetContentArea func(pointer) pointer + gtkDialogRun func(pointer) int + gtkDialogSetDefaultResponse func(pointer, int) + gtkDragDestSet func(pointer, uint, pointer, uint, uint) + gtkFileChooserAddFilter func(pointer, pointer) + gtkFileChooserDialogNew func(string, pointer, int, string, int, string, int, pointer) pointer + gtkFileChooserGetFilenames func(pointer) *GSList + gtkFileChooserSetAction func(pointer, int) + gtkFileChooserSetCreateFolders func(pointer, bool) + gtkFileChooserSetCurrentFolder func(pointer, string) + gtkFileChooserSetSelectMultiple func(pointer, bool) + gtkFileChooserSetShowHidden func(pointer, bool) + gtkFileFilterAddPattern func(pointer, string) + gtkFileFilterNew func() pointer + gtkFileFilterSetName func(pointer, string) + gtkImageNewFromPixbuf func(pointer) pointer + gtkMenuBarNew func() pointer + gtkMenuItemNewWithLabel func(string) pointer + gtkMenuItemSetLabel func(pointer, string) + gtkMenuItemSetSubmenu func(pointer, pointer) + gtkMenuNew func() pointer + gtkMenuShellAppend func(pointer, pointer) + gtkMessageDialogNew func(pointer, int, int, int, string) pointer + gtkRadioMenuItemGetGroup func(pointer) GSListPointer + gtkRadioMenuItemNewWithLabel func(GSListPointer, string) pointer + gtkSeparatorMenuItemNew func() pointer + gtkStyleContextAddProvider func(pointer, pointer, int) + gtkTargetEntryFree func(pointer) + gtkTargetEntryNew func(string, int, uint) pointer + gtkWidgetDestroy func(pointer) + gtkWidgetGetDisplay func(pointer) pointer + gtkWidgetGetScreen func(pointer) pointer + gtkWidgetGetStyleContext func(pointer) pointer + gtkWidgetGetWindow func(pointer) pointer + gtkWidgetHide func(pointer) + gtkWidgetIsVisible func(pointer) bool + gtkWidgetShow func(pointer) + gtkWidgetShowAll func(pointer) + gtkWidgetSetAppPaintable func(pointer, int) + gtkWidgetSetName func(pointer, string) + gtkWidgetSetSensitive func(pointer, int) + gtkWidgetSetToolTipText func(pointer, string) + gtkWidgetSetVisual func(pointer, pointer) + gtkWindowClose func(pointer) + gtkWindowFullScreen func(pointer) + gtkWindowGetPosition func(pointer, *int, *int) bool + gtkWindowGetSize func(pointer, *int, *int) + gtkWindowHasToplevelFocus func(pointer) int + gtkWindowKeepAbove func(pointer, bool) + gtkWindowMaximize func(pointer) + gtkWindowMinimize func(pointer) + gtkWindowMove func(pointer, int, int) + gtkWindowPresent func(pointer) + gtkWindowResize func(pointer, int, int) + gtkWindowSetDecorated func(pointer, int) + gtkWindowSetGeometryHints func(pointer, pointer, pointer, int) + gtkWindowSetKeepAbove func(pointer, bool) + gtkWindowSetResizable func(pointer, bool) + gtkWindowSetTitle func(pointer, string) + gtkWindowUnfullscreen func(pointer) + gtkWindowUnmaximize func(pointer) + + // webkit + webkitNewWithUserContentManager func(pointer) pointer + webkitRegisterUriScheme func(pointer, string, pointer, int, int) + webkitSettingsGetEnableDeveloperExtras func(pointer) bool + webkitSettingsSetHardwareAccelerationPolicy func(pointer, int) + webkitSettingsSetEnableDeveloperExtras func(pointer, bool) + webkitSettingsSetUserAgentWithApplicationDetails func(pointer, string, string) + webkitUserContentManagerNew func() pointer + webkitUserContentManagerRegisterScriptMessageHandler func(pointer, string) + webkitWebContextGetDefault func() pointer + webkitWebViewEvaluateJS func(pointer, string, int, pointer, string, pointer, pointer, pointer) + webkitWebViewGetSettings func(pointer) pointer + webkitWebViewGetZoom func(pointer) float64 + webkitWebViewLoadAlternateHTML func(pointer, string, string, *string) + webkitWebViewLoadUri func(pointer, string) + webkitWebViewSetBackgroundColor func(pointer, pointer) + webkitWebViewSetSettings func(pointer, pointer) + webkitWebViewSetZoomLevel func(pointer, float64) +) + +func init() { + // needed for GTK4 to function + _ = os.Setenv("GDK_BACKEND", "x11") + var err error + + // gtk, err = purego.Dlopen(gtk4, purego.RTLD_NOW|purego.RTLD_GLOBAL) + // if err == nil { + // version = 4 + // return + // } + + // log.Println("Failed to open GTK4: Falling back to GTK3") + + gtk, err = purego.Dlopen(gtk3, purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } + gtkVersion = 3 + + webkit, err = purego.Dlopen(webkit4, purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } + + // Function registration + // GLib + purego.RegisterLibFunc(&gApplicationHold, gtk, "g_application_hold") + purego.RegisterLibFunc(&gApplicationName, gtk, "g_get_application_name") + purego.RegisterLibFunc(&gApplicationQuit, gtk, "g_application_quit") + purego.RegisterLibFunc(&gApplicationRelease, gtk, "g_application_release") + purego.RegisterLibFunc(&gApplicationRun, gtk, "g_application_run") + purego.RegisterLibFunc(&gBytesNewStatic, gtk, "g_bytes_new_static") + purego.RegisterLibFunc(&gBytesUnref, gtk, "g_bytes_unref") + purego.RegisterLibFunc(&gFree, gtk, "g_free") + purego.RegisterLibFunc(&gIdleAdd, gtk, "g_idle_add") + purego.RegisterLibFunc(&gObjectRefSink, gtk, "g_object_ref_sink") + purego.RegisterLibFunc(&gObjectUnref, gtk, "g_object_unref") + purego.RegisterLibFunc(&gSignalConnectData, gtk, "g_signal_connect_data") + purego.RegisterLibFunc(&gSignalConnectObject, gtk, "g_signal_connect_object") + purego.RegisterLibFunc(&gSignalHandlerBlock, gtk, "g_signal_handler_block") + purego.RegisterLibFunc(&gSignalHandlerUnblock, gtk, "g_signal_handler_unblock") + purego.RegisterLibFunc(&gThreadSelf, gtk, "g_thread_self") + + // GDK + purego.RegisterLibFunc(&gdkDisplayGetMonitor, gtk, "gdk_display_get_monitor") + purego.RegisterLibFunc(&gdkDisplayGetMonitorAtWindow, gtk, "gdk_display_get_monitor_at_window") + purego.RegisterLibFunc(&gdkDisplayGetNMonitors, gtk, "gdk_display_get_n_monitors") + purego.RegisterLibFunc(&gdkMonitorGetGeometry, gtk, "gdk_monitor_get_geometry") + purego.RegisterLibFunc(&gdkMonitorGetScaleFactor, gtk, "gdk_monitor_get_scale_factor") + purego.RegisterLibFunc(&gdkMonitorIsPrimary, gtk, "gdk_monitor_is_primary") + purego.RegisterLibFunc(&gdkPixbufNewFromBytes, gtk, "gdk_pixbuf_new_from_bytes") + purego.RegisterLibFunc(&gdkRgbaParse, gtk, "gdk_rgba_parse") + purego.RegisterLibFunc(&gdkScreenGetRgbaVisual, gtk, "gdk_screen_get_rgba_visual") + purego.RegisterLibFunc(&gdkScreenIsComposited, gtk, "gdk_screen_is_composited") + purego.RegisterLibFunc(&gdkWindowGetDisplay, gtk, "gdk_window_get_display") + purego.RegisterLibFunc(&gdkWindowGetState, gtk, "gdk_window_get_state") + + // GTK3 + purego.RegisterLibFunc(>kApplicationNew, gtk, "gtk_application_new") + purego.RegisterLibFunc(>kApplicationGetActiveWindow, gtk, "gtk_application_get_active_window") + purego.RegisterLibFunc(>kApplicationGetWindows, gtk, "gtk_application_get_windows") + purego.RegisterLibFunc(>kApplicationWindowNew, gtk, "gtk_application_window_new") + purego.RegisterLibFunc(>kBoxNew, gtk, "gtk_box_new") + purego.RegisterLibFunc(>kBoxPackStart, gtk, "gtk_box_pack_start") + purego.RegisterLibFunc(>kCheckMenuItemGetActive, gtk, "gtk_check_menu_item_get_active") + purego.RegisterLibFunc(>kCheckMenuItemNewWithLabel, gtk, "gtk_check_menu_item_new_with_label") + purego.RegisterLibFunc(>kCheckMenuItemSetActive, gtk, "gtk_check_menu_item_set_active") + purego.RegisterLibFunc(>kContainerAdd, gtk, "gtk_container_add") + purego.RegisterLibFunc(>kCSSProviderLoadFromData, gtk, "gtk_css_provider_load_from_data") + purego.RegisterLibFunc(>kDialogAddButton, gtk, "gtk_dialog_add_button") + purego.RegisterLibFunc(>kDialogGetContentArea, gtk, "gtk_dialog_get_content_area") + purego.RegisterLibFunc(>kDialogRun, gtk, "gtk_dialog_run") + purego.RegisterLibFunc(>kDialogSetDefaultResponse, gtk, "gtk_dialog_set_default_response") + purego.RegisterLibFunc(>kDragDestSet, gtk, "gtk_drag_dest_set") + purego.RegisterLibFunc(>kFileChooserAddFilter, gtk, "gtk_file_chooser_add_filter") + purego.RegisterLibFunc(>kFileChooserDialogNew, gtk, "gtk_file_chooser_dialog_new") + purego.RegisterLibFunc(>kFileChooserGetFilenames, gtk, "gtk_file_chooser_get_filenames") + purego.RegisterLibFunc(>kFileChooserSetAction, gtk, "gtk_file_chooser_set_action") + purego.RegisterLibFunc(>kFileChooserSetCreateFolders, gtk, "gtk_file_chooser_set_create_folders") + purego.RegisterLibFunc(>kFileChooserSetCurrentFolder, gtk, "gtk_file_chooser_set_current_folder") + purego.RegisterLibFunc(>kFileChooserSetSelectMultiple, gtk, "gtk_file_chooser_set_select_multiple") + purego.RegisterLibFunc(>kFileChooserSetShowHidden, gtk, "gtk_file_chooser_set_show_hidden") + purego.RegisterLibFunc(>kFileFilterAddPattern, gtk, "gtk_file_filter_add_pattern") + purego.RegisterLibFunc(>kFileFilterNew, gtk, "gtk_file_filter_new") + purego.RegisterLibFunc(>kFileFilterSetName, gtk, "gtk_file_filter_set_name") + purego.RegisterLibFunc(>kImageNewFromPixbuf, gtk, "gtk_image_new_from_pixbuf") + purego.RegisterLibFunc(>kMenuItemSetLabel, gtk, "gtk_menu_item_set_label") + purego.RegisterLibFunc(>kMenuBarNew, gtk, "gtk_menu_bar_new") + purego.RegisterLibFunc(>kMenuItemNewWithLabel, gtk, "gtk_menu_item_new_with_label") + purego.RegisterLibFunc(>kMenuItemSetSubmenu, gtk, "gtk_menu_item_set_submenu") + purego.RegisterLibFunc(>kMenuNew, gtk, "gtk_menu_new") + purego.RegisterLibFunc(>kMenuShellAppend, gtk, "gtk_menu_shell_append") + purego.RegisterLibFunc(>kMessageDialogNew, gtk, "gtk_message_dialog_new") + purego.RegisterLibFunc(>kRadioMenuItemGetGroup, gtk, "gtk_radio_menu_item_get_group") + purego.RegisterLibFunc(>kRadioMenuItemNewWithLabel, gtk, "gtk_radio_menu_item_new_with_label") + purego.RegisterLibFunc(>kSeparatorMenuItemNew, gtk, "gtk_separator_menu_item_new") + purego.RegisterLibFunc(>kStyleContextAddProvider, gtk, "gtk_style_context_add_provider") + purego.RegisterLibFunc(>kTargetEntryFree, gtk, "gtk_target_entry_free") + purego.RegisterLibFunc(>kTargetEntryNew, gtk, "gtk_target_entry_new") + purego.RegisterLibFunc(>kWidgetDestroy, gtk, "gtk_widget_destroy") + purego.RegisterLibFunc(>kWidgetGetDisplay, gtk, "gtk_widget_get_display") + purego.RegisterLibFunc(>kWidgetGetScreen, gtk, "gtk_widget_get_screen") + purego.RegisterLibFunc(>kWidgetGetStyleContext, gtk, "gtk_widget_get_style_context") + purego.RegisterLibFunc(>kWidgetGetWindow, gtk, "gtk_widget_get_window") + purego.RegisterLibFunc(>kWidgetHide, gtk, "gtk_widget_hide") + purego.RegisterLibFunc(>kWidgetIsVisible, gtk, "gtk_widget_is_visible") + purego.RegisterLibFunc(>kWidgetSetAppPaintable, gtk, "gtk_widget_set_app_paintable") + purego.RegisterLibFunc(>kWidgetSetName, gtk, "gtk_widget_set_name") + purego.RegisterLibFunc(>kWidgetSetSensitive, gtk, "gtk_widget_set_sensitive") + purego.RegisterLibFunc(>kWidgetSetToolTipText, gtk, "gtk_widget_set_tooltip_text") + purego.RegisterLibFunc(>kWidgetSetVisual, gtk, "gtk_widget_set_visual") + purego.RegisterLibFunc(>kWidgetShow, gtk, "gtk_widget_show") + purego.RegisterLibFunc(>kWidgetShowAll, gtk, "gtk_widget_show_all") + purego.RegisterLibFunc(>kWindowFullScreen, gtk, "gtk_window_fullscreen") + purego.RegisterLibFunc(>kWindowClose, gtk, "gtk_window_close") + purego.RegisterLibFunc(>kWindowGetPosition, gtk, "gtk_window_get_position") + purego.RegisterLibFunc(>kWindowGetSize, gtk, "gtk_window_get_size") + purego.RegisterLibFunc(>kWindowMaximize, gtk, "gtk_window_maximize") + purego.RegisterLibFunc(>kWindowMove, gtk, "gtk_window_move") + purego.RegisterLibFunc(>kWindowPresent, gtk, "gtk_window_present") + //purego.RegisterLibFunc(>kWindowPresent, gtk, "gtk_window_unminimize") // gtk4 + purego.RegisterLibFunc(>kWindowHasToplevelFocus, gtk, "gtk_window_has_toplevel_focus") + purego.RegisterLibFunc(>kWindowMinimize, gtk, "gtk_window_iconify") // gtk3 + // purego.RegisterLibFunc(>kWindowMinimize, gtk, "gtk_window_minimize") // gtk4 + purego.RegisterLibFunc(>kWindowResize, gtk, "gtk_window_resize") + purego.RegisterLibFunc(>kWindowSetGeometryHints, gtk, "gtk_window_set_geometry_hints") + purego.RegisterLibFunc(>kWindowSetDecorated, gtk, "gtk_window_set_decorated") + purego.RegisterLibFunc(>kWindowKeepAbove, gtk, "gtk_window_set_keep_above") + purego.RegisterLibFunc(>kWindowSetResizable, gtk, "gtk_window_set_resizable") + purego.RegisterLibFunc(>kWindowSetTitle, gtk, "gtk_window_set_title") + purego.RegisterLibFunc(>kWindowUnfullscreen, gtk, "gtk_window_unfullscreen") + purego.RegisterLibFunc(>kWindowUnmaximize, gtk, "gtk_window_unmaximize") + + // webkit + purego.RegisterLibFunc(&webkitNewWithUserContentManager, webkit, "webkit_web_view_new_with_user_content_manager") + purego.RegisterLibFunc(&webkitRegisterUriScheme, webkit, "webkit_web_context_register_uri_scheme") + purego.RegisterLibFunc(&webkitSettingsGetEnableDeveloperExtras, webkit, "webkit_settings_get_enable_developer_extras") + purego.RegisterLibFunc(&webkitSettingsSetEnableDeveloperExtras, webkit, "webkit_settings_set_enable_developer_extras") + purego.RegisterLibFunc(&webkitSettingsSetHardwareAccelerationPolicy, webkit, "webkit_settings_set_hardware_acceleration_policy") + purego.RegisterLibFunc(&webkitSettingsSetUserAgentWithApplicationDetails, webkit, "webkit_settings_set_user_agent_with_application_details") + purego.RegisterLibFunc(&webkitUserContentManagerNew, webkit, "webkit_user_content_manager_new") + purego.RegisterLibFunc(&webkitUserContentManagerRegisterScriptMessageHandler, webkit, "webkit_user_content_manager_register_script_message_handler") + purego.RegisterLibFunc(&webkitWebContextGetDefault, webkit, "webkit_web_context_get_default") + purego.RegisterLibFunc(&webkitWebViewEvaluateJS, webkit, "webkit_web_view_evaluate_javascript") + purego.RegisterLibFunc(&webkitWebViewGetSettings, webkit, "webkit_web_view_get_settings") + purego.RegisterLibFunc(&webkitWebViewGetZoom, webkit, "webkit_web_view_get_zoom_level") + purego.RegisterLibFunc(&webkitWebViewLoadAlternateHTML, webkit, "webkit_web_view_load_alternate_html") + purego.RegisterLibFunc(&webkitWebViewLoadUri, webkit, "webkit_web_view_load_uri") + purego.RegisterLibFunc(&webkitWebViewSetBackgroundColor, webkit, "webkit_web_view_set_background_color") + purego.RegisterLibFunc(&webkitWebViewSetSettings, webkit, "webkit_web_view_set_settings") + purego.RegisterLibFunc(&webkitWebViewSetZoomLevel, webkit, "webkit_web_view_set_zoom_level") +} + +// mainthread stuff +func dispatchOnMainThread(id uint) { + gIdleAdd(purego.NewCallback(func(pointer) int { + executeOnMainThread(id) + return GSourceRemove + })) +} + +// implementation below +func appName() string { + return gApplicationName() +} + +func appNew(name string) pointer { + GApplicationDefaultFlags := uint(0) + + name = strings.ToLower(name) + if name == "" { + name = "undefined" + } + identifier := fmt.Sprintf("org.wails.%s", strings.Replace(name, " ", "-", -1)) + + return pointer(gtkApplicationNew(identifier, GApplicationDefaultFlags)) +} + +func appRun(application pointer) error { + mainThreadId = gThreadSelf() + fmt.Println("linux_purego: appRun threadID", mainThreadId) + + app := pointer(application) + activate := func() { + // TODO: Do we care? + fmt.Println("linux.activated!") + gApplicationHold(app) // allow running without a window + } + gSignalConnectData( + application, + "activate", + purego.NewCallback(activate), + app, + false, + 0) + + status := gApplicationRun(app, 0, nil) + gApplicationRelease(app) + gObjectUnref(app) + + var err error + if status != 0 { + err = fmt.Errorf("exit code: %d", status) + } + return err +} + +func appDestroy(application pointer) { + gApplicationQuit(pointer(application)) +} + +func getCurrentWindowID(application pointer, windows map[windowPointer]uint) uint { + // TODO: Add extra metadata to window and use it! + window := gtkApplicationGetActiveWindow(pointer(application)) + identifier, ok := windows[windowPointer(window)] + if ok { + return identifier + } + // FIXME: Should we panic here if not found? + return uint(1) +} + +type GList struct { + data pointer + next *GList + prev *GList +} + +func getWindows(application pointer) []pointer { + result := []pointer{} + windows := gtkApplicationGetWindows(pointer(application)) + // FIXME: Need to make a struct here to deal with response data + for { + result = append(result, pointer(windows.data)) + windows = windows.next + if windows == nil { + return result + } + } +} + +func hideAllWindows(application pointer) { + for _, window := range getWindows(application) { + gtkWidgetHide(window) + } +} + +func showAllWindows(application pointer) { + for _, window := range getWindows(application) { + gtkWidgetShowAll(window) + } +} + +// Menu +func menuAddSeparator(menu *Menu) { + gtkMenuShellAppend( + pointer((menu.impl).(*linuxMenu).native), + gtkSeparatorMenuItemNew()) +} + +func menuAppend(parent *Menu, menu *MenuItem) { + // TODO: override this with the GTK4 version if needed - possibly rename to imply it's an alias + gtkMenuShellAppend( + pointer((parent.impl).(*linuxMenu).native), + pointer((menu.impl).(*linuxMenuItem).native)) + + /* gtk4 + C.gtk_menu_item_set_submenu( + (*C.struct__GtkMenuItem)((menu.impl).(*linuxMenuItem).native), + (*C.struct__GtkWidget)((parent.impl).(*linuxMenu).native), + ) + */ +} + +func menuBarNew() pointer { + return pointer(gtkMenuBarNew()) +} + +func menuNew() pointer { + return pointer(gtkMenuNew()) +} + +func menuSetSubmenu(item *MenuItem, menu *Menu) { + // FIXME: How is this different than `menuAppend` above? + gtkMenuItemSetSubmenu( + pointer((item.impl).(*linuxMenuItem).native), + pointer((menu.impl).(*linuxMenu).native)) +} + +func menuGetRadioGroup(item *linuxMenuItem) *GSList { + return (*GSList)(gtkRadioMenuItemGetGroup(pointer(item.native))) +} + +func attachMenuHandler(item *MenuItem) { + handleClick := func() { + item := item + switch item.itemType { + case text, checkbox: + menuItemClicked <- item.id + case radio: + menuItem := (item.impl).(*linuxMenuItem) + if menuItem.isChecked() { + menuItemClicked <- item.id + } + } + } + + impl := (item.impl).(*linuxMenuItem) + widget := impl.native + flags := 0 + handlerId := gSignalConnectObject( + pointer(widget), + "activate", + pointer(purego.NewCallback(handleClick)), + pointer(widget), + flags) + impl.handlerId = uint(handlerId) +} + +// menuItem +func menuItemChecked(widget pointer) bool { + if gtkCheckMenuItemGetActive(widget) == 1 { + return true + } + return false +} + +func menuItemNew(label string) pointer { + return pointer(gtkMenuItemNewWithLabel(label)) +} + +func menuCheckItemNew(label string) pointer { + return pointer(gtkCheckMenuItemNewWithLabel(label)) +} + +func menuItemSetChecked(widget pointer, checked bool) { + value := 0 + if checked { + value = 1 + } + gtkCheckMenuItemSetActive(pointer(widget), value) +} + +func menuItemSetDisabled(widget pointer, disabled bool) { + value := 1 + if disabled { + value = 0 + } + gtkWidgetSetSensitive(widget, value) +} + +func menuItemSetLabel(widget pointer, label string) { + gtkMenuItemSetLabel( + pointer(widget), + label) +} + +func menuItemSetToolTip(widget pointer, tooltip string) { + gtkWidgetSetToolTipText( + pointer(widget), + tooltip) +} + +func menuItemSignalBlock(widget pointer, handlerId uint, block bool) { + if block { + gSignalHandlerBlock(widget, handlerId) + } else { + gSignalHandlerUnblock(widget, handlerId) + } +} + +func menuRadioItemNew(group GSListPointer, label string) pointer { + return pointer(gtkRadioMenuItemNewWithLabel(group, label)) +} + +// screen related + +func getScreenByIndex(display pointer, index int) *Screen { + monitor := gdkDisplayGetMonitor(display, index) + // TODO: Do we need to update Screen to contain current info? + // currentMonitor := C.gdk_display_get_monitor_at_window(display, window) + + geometry := struct { + x int32 + y int32 + width int32 + height int32 + }{} + result := pointer(unsafe.Pointer(&geometry)) + gdkMonitorGetGeometry(monitor, result) + + primary := false + if gdkMonitorIsPrimary(monitor) == 1 { + primary = true + } + + return &Screen{ + IsPrimary: primary, + ScaleFactor: 1.0, + X: int(geometry.x), + Y: int(geometry.y), + Size: Size{ + Height: int(geometry.height), + Width: int(geometry.width), + }, + } +} + +func getScreens(app pointer) ([]*Screen, error) { + var screens []*Screen + window := gtkApplicationGetActiveWindow(app) + display := gdkWindowGetDisplay(window) + count := gdkDisplayGetNMonitors(display) + for i := 0; i < int(count); i++ { + screens = append(screens, getScreenByIndex(display, i)) + } + return screens, nil +} + +// widgets +func widgetSetSensitive(widget pointer, enabled bool) { + value := 0 + if enabled { + value = 1 + } + gtkWidgetSetSensitive(widget, value) +} + +func widgetSetVisible(widget pointer, hidden bool) { + if hidden { + gtkWidgetHide(widget) + } else { + gtkWidgetShow(widget) + } +} + +// window related functions +func windowClose(window pointer) { + gtkWindowClose(window) +} + +func windowEnableDND(id uint, webview pointer) { + targetentry := gtkTargetEntryNew("text/uri-list", 0, id) + defer gtkTargetEntryFree(targetentry) + + GtkDestDefaultDrop := uint(0) + GdkActionCopy := uint(0) //? + gtkDragDestSet(webview, GtkDestDefaultDrop, targetentry, 1, GdkActionCopy) + + // FIXME: enable and process + /* gSignalConnectData(webview, + "drag-data-received", + purego.NewCallback(onDragNDrop), + 0, + false, + 0)*/ +} + +func windowExecJS(webview pointer, js string) { + webkitWebViewEvaluateJS( + webview, + js, + len(js), + 0, + "", + 0, + 0, + 0) +} + +func windowDestroy(window pointer) { + // Should this truly 'destroy' ? + gtkWindowClose(window) +} + +func windowFullscreen(window pointer) { + gtkWindowFullScreen(window) +} + +func windowGetPosition(window pointer) (int, int) { + var x, y int + gtkWindowGetPosition(window, &x, &y) + return x, y +} + +func windowGetCurrentMonitor(window pointer) pointer { + // Get the monitor that the window is currently on + display := gtkWidgetGetDisplay(window) + window = gtkWidgetGetWindow(window) + if window == 0 { + return 0 + } + return gdkDisplayGetMonitorAtWindow(display, window) +} + +func windowGetCurrentMonitorGeometry(window pointer) (x int, y int, width int, height int, scaleFactor int) { + monitor := windowGetCurrentMonitor(window) + if monitor == 0 { + return -1, -1, -1, -1, 1 + } + + result := struct { + x int32 + y int32 + width int32 + height int32 + }{} + gdkMonitorGetGeometry(monitor, pointer(unsafe.Pointer(&result))) + return int(result.x), int(result.y), int(result.width), int(result.height), gdkMonitorGetScaleFactor(monitor) +} + +func windowGetRelativePosition(window pointer) (int, int) { + absX, absY := windowGetPosition(window) + x, y, _, _, _ := windowGetCurrentMonitorGeometry(window) + + relX := absX - x + relY := absY - y + + // TODO: Scale based on DPI + return relX, relY +} + +func windowGetSize(window pointer) (int, int) { + // TODO: dispatchOnMainThread? + var width, height int + gtkWindowGetSize(window, &width, &height) + return width, height +} + +func windowGetPosition(window pointer) (int, int) { + // TODO: dispatchOnMainThread? + var x, y int + gtkWindowGetPosition(window, &x, &y) + return x, y +} + +func windowHide(window pointer) { + gtkWidgetHide(window) +} + +func windowIsFocused(window pointer) bool { + return gtkWindowHasToplevelFocus(window) == 1 +} + +func windowIsFullscreen(window pointer) bool { + gdkwindow := gtkWidgetGetWindow(window) + state := gdkWindowGetState(gdkwindow) + return state&GdkWindowStateFullscreen > 0 +} + +func windowIsMaximized(window pointer) bool { + gdkwindow := gtkWidgetGetWindow(window) + state := gdkWindowGetState(gdkwindow) + return state&GdkWindowStateMaximized > 0 && state&GdkWindowStateFullscreen == 0 +} + +func windowIsMinimized(window pointer) bool { + gdkwindow := gtkWidgetGetWindow(window) + state := gdkWindowGetState(gdkwindow) + return state&GdkWindowStateIconified > 0 +} + +func windowIsVisible(window pointer) bool { + // TODO: validate this works.. (used a `bool` in the registration) + return gtkWidgetIsVisible(window) +} + +func windowMaximize(window pointer) { + gtkWindowMaximize(window) +} + +func windowMinimize(window pointer) { + gtkWindowMinimize(window) +} + +func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (pointer, pointer, pointer) { + window := gtkApplicationWindowNew(application) + gObjectRefSink(window) + webview := windowNewWebview(windowId, gpuPolicy) + vbox := gtkBoxNew(GtkOrientationVertical, 0) + gtkContainerAdd(window, vbox) + gtkWidgetSetName(vbox, "webview-box") + + if menu != 0 { + gtkBoxPackStart(vbox, menu, 0, 0, 0) + } + gtkBoxPackStart(vbox, webview, 1, 1, 0) + return pointer(window), pointer(webview), pointer(vbox) +} + +func windowNewWebview(parentId uint, gpuPolicy int) pointer { + manager := webkitUserContentManagerNew() + webkitUserContentManagerRegisterScriptMessageHandler(manager, "external") + wv := webkitNewWithUserContentManager(manager) + if !registered { + webkitRegisterUriScheme( + webkitWebContextGetDefault(), + "wails", + pointer(purego.NewCallback(func(request uintptr) { + webviewRequests <- &webViewAssetRequest{ + Request: webview.NewRequest(request), + windowId: parentId, + windowName: func() string { + if window, ok := globalApplication.Window.GetByID(parentId); ok { + return window.Name() + } + return "" + }(), + } + })), + 0, + 0, + ) + registered = true + } + + settings := webkitWebViewGetSettings(wv) + webkitSettingsSetUserAgentWithApplicationDetails( + settings, + "wails.io", + "") + webkitSettingsSetHardwareAccelerationPolicy(settings, gpuPolicy) + webkitWebViewSetSettings(wv, settings) + return wv +} + +func windowPresent(window pointer) { + gtkWindowPresent(pointer(window)) +} + +func windowReload(webview pointer, address string) { + webkitWebViewLoadUri(pointer(webview), address) +} + +func windowResize(window pointer, width, height int) { + gtkWindowResize(window, width, height) +} + +func windowShow(window pointer) { + gtkWidgetShowAll(pointer(window)) +} + +func windowSetBackgroundColour(vbox, webview pointer, colour RGBA) { + const GtkStyleProviderPriorityUser = 800 + + // FIXME: Use a struct! + rgba := make([]byte, 4*8) // C.sizeof_GdkRGBA == 32 + rgbaPointer := pointer(unsafe.Pointer(&rgba[0])) + if !gdkRgbaParse( + rgbaPointer, + fmt.Sprintf("rgba(%v,%v,%v,%v)", + colour.Red, + colour.Green, + colour.Blue, + float32(colour.Alpha)/255.0, + )) { + return + } + webkitWebViewSetBackgroundColor(pointer(webview), rgbaPointer) + + colour.Alpha = 255 + css := fmt.Sprintf("#webview-box {background-color: rgba(%d, %d, %d, %1.1f);}", colour.Red, colour.Green, colour.Blue, float32(colour.Alpha)/255.0) + provider := gtkCSSProviderNew() + defer gObjectUnref(provider) + gtkStyleContextAddProvider( + gtkWidgetGetStyleContext(vbox), + provider, + GtkStyleProviderPriorityUser, + ) + gtkCSSProviderLoadFromData(provider, css, -1, 0) +} + +func windowSetGeometryHints(window pointer, minWidth, minHeight, maxWidth, maxHeight int) { + size := GdkGeometry{ + minWidth: int32(minWidth), + minHeight: int32(minHeight), + maxWidth: int32(maxWidth), + maxHeight: int32(maxHeight), + } + gtkWindowSetGeometryHints( + pointer(window), + pointer(0), + pointer(unsafe.Pointer(&size)), + GdkHintMinSize|GdkHintMaxSize) +} + +func windowSetFrameless(window pointer, frameless bool) { + decorated := 1 + if frameless { + decorated = 0 + } + gtkWindowSetDecorated(pointer(window), decorated) + + // TODO: Deal with transparency for the titlebar if possible when !frameless + // Perhaps we just make it undecorated and add a menu bar inside? +} + +// TODO: confirm this is working properly +func windowSetHTML(webview pointer, html string) { + webkitWebViewLoadAlternateHTML(webview, html, "wails://", nil) +} + +func windowSetKeepAbove(window pointer, alwaysOnTop bool) { + gtkWindowKeepAbove(window, alwaysOnTop) +} + +func windowSetResizable(window pointer, resizable bool) { + // FIXME: Does this work? + gtkWindowSetResizable( + pointer(window), + resizable, + ) +} + +func windowSetTitle(window pointer, title string) { + gtkWindowSetTitle(pointer(window), title) +} + +func windowSetTransparent(window pointer) { + screen := gtkWidgetGetScreen(pointer(window)) + visual := gdkScreenGetRgbaVisual(screen) + if visual == 0 { + return + } + if gdkScreenIsComposited(screen) == 1 { + gtkWidgetSetAppPaintable(pointer(window), 1) + gtkWidgetSetVisual(pointer(window), visual) + } +} + +func windowSetURL(webview pointer, uri string) { + webkitWebViewLoadUri(webview, uri) +} + +func windowSetupSignalHandlers(windowId uint, window, webview pointer, emit func(e events.WindowEventType)) { + handleDelete := purego.NewCallback(func(pointer) { + emit(events.Common.WindowClosing) + }) + gSignalConnectData(window, "delete-event", handleDelete, 0, false, 0) + + /* + event = C.CString("load-changed") + defer C.free(unsafe.Pointer(event)) + C.signal_connect(webview, event, C.webviewLoadChanged, unsafe.Pointer(&w.parent.id)) + */ + + // TODO: Handle mouse button / drag events + /* id := C.uint(windowId) + event = C.CString("button-press-event") + C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, C.onButtonEvent, unsafe.Pointer(&id)) + C.free(unsafe.Pointer(event)) + event = C.CString("button-release-event") + defer C.free(unsafe.Pointer(event)) + C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, onButtonEvent, unsafe.Pointer(&id)) + */ +} + +func windowOpenDevTools(webview pointer) { + settings := webkitWebViewGetSettings(pointer(webview)) + webkitSettingsSetEnableDeveloperExtras( + settings, + !webkitSettingsGetEnableDeveloperExtras(settings)) +} + +func windowUnfullscreen(window pointer) { + gtkWindowUnfullscreen(window) +} + +func windowUnmaximize(window pointer) { + gtkWindowUnmaximize(window) +} + +func windowZoom(webview pointer) float64 { + return webkitWebViewGetZoom(webview) +} + +func windowZoomIn(webview pointer) { + ZoomInFactor := 1.10 + windowZoomSet(webview, windowZoom(webview)*ZoomInFactor) +} +func windowZoomOut(webview pointer) { + ZoomOutFactor := -1.10 + windowZoomSet(webview, windowZoom(webview)*ZoomOutFactor) +} + +func windowZoomSet(webview pointer, zoom float64) { + if zoom < 1.0 { // 1.0 is the smallest allowable + zoom = 1.0 + } + webkitWebViewSetZoomLevel(webview, zoom) +} + +func windowMove(window pointer, x, y int) { + gtkWindowMove(window, x, y) +} + +func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden bool, currentFolder, title string, action int, acceptLabel string, filters []FileFilter) ([]string, error) { + GtkResponseCancel := 0 + GtkResponseAccept := 1 + + fc := gtkFileChooserDialogNew( + title, + window, + action, + "_Cancel", + GtkResponseCancel, + acceptLabel, + GtkResponseAccept, + 0) + + gtkFileChooserSetAction(fc, action) + + gtkFilters := []pointer{} + for _, filter := range filters { + f := gtkFileFilterNew() + gtkFileFilterSetName(f, filter.DisplayName) + gtkFileFilterAddPattern(f, filter.Pattern) + gtkFileChooserAddFilter(fc, f) + gtkFilters = append(gtkFilters, f) + } + gtkFileChooserSetSelectMultiple(fc, allowMultiple) + gtkFileChooserSetCreateFolders(fc, createFolders) + gtkFileChooserSetShowHidden(fc, showHidden) + + if currentFolder != "" { + gtkFileChooserSetCurrentFolder(fc, currentFolder) + } + + buildStringAndFree := func(s pointer) string { + bytes := []byte{} + p := unsafe.Pointer(s) + for { + val := *(*byte)(p) + if val == 0 { // this is the null terminator + break + } + bytes = append(bytes, val) + p = unsafe.Add(p, 1) + } + gFree(s) // so we don't have to iterate a second time + return string(bytes) + } + + response := gtkDialogRun(fc) + selections := []string{} + if response == GtkResponseAccept { + filenames := gtkFileChooserGetFilenames(fc) + iter := filenames + count := 0 + for { + selections = append(selections, buildStringAndFree(iter.data)) + iter = iter.next + if iter == nil || count == 1024 { + break + } + count++ + } + } + defer gtkWidgetDestroy(fc) + return selections, nil +} + +// dialog related +func runOpenFileDialog(dialog *OpenFileDialogStruct) ([]string, error) { + const GtkFileChooserActionOpen = 0 + const GtkFileChooserActionSelectFolder = 2 + + var action int + + if dialog.canChooseDirectories { + action = GtkFileChooserActionSelectFolder + } else { + action = GtkFileChooserActionOpen + } + + window := pointer(0) + if dialog.window != nil { + nativeWindow := dialog.window.NativeWindow() + if nativeWindow != nil { + window = pointer(uintptr(nativeWindow)) + } + } + + buttonText := dialog.buttonText + if buttonText == "" { + buttonText = "_Open" + } + + return runChooserDialog( + window, + dialog.allowsMultipleSelection, + dialog.canCreateDirectories, + dialog.showHiddenFiles, + dialog.directory, + dialog.title, + GtkFileChooserActionOpen, + buttonText, + dialog.filters) +} + +func runQuestionDialog(parent pointer, options *MessageDialog) int { + dType, ok := map[DialogType]int{ + InfoDialogType: GtkMessageInfo, + WarningDialogType: GtkMessageWarning, + QuestionDialogType: GtkMessageQuestion, + }[options.DialogType] + if !ok { + // FIXME: Add logging here! + dType = GtkMessageInfo + } + buttonMask := GtkButtonsOk + if len(options.Buttons) > 0 { + buttonMask = GtkButtonsNone + } + + dialog := gtkMessageDialogNew( + pointer(parent), + GtkDialogModal|GtkDialogDestroyWithParent, + dType, + buttonMask, + options.Message) + + if options.Title != "" { + gtkWindowSetTitle(dialog, options.Title) + } + + GdkColorspaceRGB := 0 + + if img, err := pngToImage(options.Icon); err == nil { + gbytes := gBytesNewStatic(uintptr(unsafe.Pointer(&img.Pix[0])), len(img.Pix)) + + defer gBytesUnref(gbytes) + pixBuf := gdkPixbufNewFromBytes( + gbytes, + GdkColorspaceRGB, + 1, // has_alpha + 8, + img.Bounds().Dx(), + img.Bounds().Dy(), + img.Stride, + ) + image := gtkImageNewFromPixbuf(pixBuf) + widgetSetVisible(image, false) + contentArea := gtkDialogGetContentArea(dialog) + gtkContainerAdd(contentArea, image) + } + + for i, button := range options.Buttons { + gtkDialogAddButton( + dialog, + button.Label, + i, + ) + if button.IsDefault { + gtkDialogSetDefaultResponse(dialog, i) + } + } + defer gtkWidgetDestroy(dialog) + return gtkDialogRun(dialog) +} + +func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) { + const GtkFileChooserActionSave = 1 + + window := pointer(0) + buttonText := dialog.buttonText + if buttonText == "" { + buttonText = "_Save" + } + results, err := runChooserDialog( + window, + false, // multiple selection + dialog.canCreateDirectories, + dialog.showHiddenFiles, + dialog.directory, + dialog.title, + GtkFileChooserActionSave, + buttonText, + dialog.filters) + + if err != nil || len(results) == 0 { + return "", err + } + + return results[0], nil +} + +func isOnMainThread() bool { + return mainThreadId == gThreadSelf() +} + +// linuxWebviewWindow show/hide methods for purego implementation +func (w *linuxWebviewWindow) windowShow() { + if w.window == 0 { + return + } + windowShow(w.window) +} + +func (w *linuxWebviewWindow) windowHide() { + if w.window == 0 { + return + } + windowHide(w.window) +} diff --git a/v3/pkg/application/logger_dev.go b/v3/pkg/application/logger_dev.go new file mode 100644 index 000000000..e84f49490 --- /dev/null +++ b/v3/pkg/application/logger_dev.go @@ -0,0 +1,20 @@ +//go:build !windows && !production + +package application + +import ( + "log/slog" + "os" + "time" + + "github.com/lmittmann/tint" + "github.com/mattn/go-isatty" +) + +func DefaultLogger(level slog.Leveler) *slog.Logger { + return slog.New(tint.NewHandler(os.Stderr, &tint.Options{ + TimeFormat: time.Kitchen, + NoColor: !isatty.IsTerminal(os.Stderr.Fd()), + Level: level, + })) +} diff --git a/v3/pkg/application/logger_dev_windows.go b/v3/pkg/application/logger_dev_windows.go new file mode 100644 index 000000000..20d20c376 --- /dev/null +++ b/v3/pkg/application/logger_dev_windows.go @@ -0,0 +1,21 @@ +//go:build windows && !production + +package application + +import ( + "log/slog" + "os" + "time" + + "github.com/lmittmann/tint" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +func DefaultLogger(level slog.Leveler) *slog.Logger { + return slog.New(tint.NewHandler(colorable.NewColorable(os.Stderr), &tint.Options{ + TimeFormat: time.StampMilli, + NoColor: !isatty.IsTerminal(os.Stderr.Fd()), + Level: level, + })) +} diff --git a/v3/pkg/application/logger_prod.go b/v3/pkg/application/logger_prod.go new file mode 100644 index 000000000..a748611ce --- /dev/null +++ b/v3/pkg/application/logger_prod.go @@ -0,0 +1,12 @@ +//go:build production + +package application + +import ( + "io" + "log/slog" +) + +func DefaultLogger(level slog.Leveler) *slog.Logger { + return slog.New(slog.NewTextHandler(io.Discard, nil)) +} diff --git a/v3/pkg/application/mainthread.go b/v3/pkg/application/mainthread.go new file mode 100644 index 000000000..6eb40ba9d --- /dev/null +++ b/v3/pkg/application/mainthread.go @@ -0,0 +1,87 @@ +package application + +import ( + "sync" +) + +var mainThreadFunctionStore = make(map[uint]func()) +var mainThreadFunctionStoreLock sync.RWMutex + +func generateFunctionStoreID() uint { + startID := 0 + for { + if _, ok := mainThreadFunctionStore[uint(startID)]; !ok { + return uint(startID) + } + startID++ + if startID == 0 { + Fatal("Too many functions have been dispatched to the main thread") + } + } +} + +func InvokeSync(fn func()) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + defer handlePanic() + fn() + wg.Done() + }) + wg.Wait() +} + +func InvokeSyncWithResult[T any](fn func() T) (res T) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + defer handlePanic() + res = fn() + wg.Done() + }) + wg.Wait() + return res +} + +func InvokeSyncWithError(fn func() error) (err error) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + defer handlePanic() + err = fn() + wg.Done() + }) + wg.Wait() + return +} + +func InvokeSyncWithResultAndError[T any](fn func() (T, error)) (res T, err error) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + defer handlePanic() + res, err = fn() + wg.Done() + }) + wg.Wait() + return res, err +} + +func InvokeSyncWithResultAndOther[T any, U any](fn func() (T, U)) (res T, other U) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + defer handlePanic() + res, other = fn() + wg.Done() + }) + wg.Wait() + return res, other +} + +func InvokeAsync(fn func()) { + globalApplication.dispatchOnMainThread(func() { + defer handlePanic() + fn() + }) +} diff --git a/v3/pkg/application/mainthread_darwin.go b/v3/pkg/application/mainthread_darwin.go new file mode 100644 index 000000000..70e4d70b1 --- /dev/null +++ b/v3/pkg/application/mainthread_darwin.go @@ -0,0 +1,45 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa + +#include "Cocoa/Cocoa.h" + +extern void dispatchOnMainThreadCallback(unsigned int); + +static void dispatchOnMainThread(unsigned int id) { + dispatch_async(dispatch_get_main_queue(), ^{ + dispatchOnMainThreadCallback(id); + }); +} + +static bool onMainThread() { + return [NSThread isMainThread]; +} + +*/ +import "C" + +func (m *macosApp) isOnMainThread() bool { + return bool(C.onMainThread()) +} + +func (m *macosApp) dispatchOnMainThread(id uint) { + C.dispatchOnMainThread(C.uint(id)) +} + +//export dispatchOnMainThreadCallback +func dispatchOnMainThreadCallback(callbackID C.uint) { + mainThreadFunctionStoreLock.RLock() + id := uint(callbackID) + fn := mainThreadFunctionStore[id] + if fn == nil { + Fatal("dispatchCallback called with invalid id: %v", id) + } + delete(mainThreadFunctionStore, id) + mainThreadFunctionStoreLock.RUnlock() + fn() +} diff --git a/v3/pkg/application/mainthread_linux.go b/v3/pkg/application/mainthread_linux.go new file mode 100644 index 000000000..a718688bc --- /dev/null +++ b/v3/pkg/application/mainthread_linux.go @@ -0,0 +1,18 @@ +//go:build linux + +package application + +func (a *linuxApp) dispatchOnMainThread(id uint) { + dispatchOnMainThread(id) +} + +func executeOnMainThread(callbackID uint) { + mainThreadFunctionStoreLock.RLock() + fn := mainThreadFunctionStore[callbackID] + if fn == nil { + Fatal("dispatchCallback called with invalid id: %v", callbackID) + } + delete(mainThreadFunctionStore, callbackID) + mainThreadFunctionStoreLock.RUnlock() + fn() +} diff --git a/v3/pkg/application/mainthread_windows.go b/v3/pkg/application/mainthread_windows.go new file mode 100644 index 000000000..ddee96339 --- /dev/null +++ b/v3/pkg/application/mainthread_windows.go @@ -0,0 +1,127 @@ +//go:build windows + +package application + +import ( + "runtime" + "sort" + "unsafe" + + "github.com/wailsapp/wails/v3/pkg/w32" +) + +var ( + wmInvokeCallback uint32 +) + +func init() { + wmInvokeCallback = w32.RegisterWindowMessage(w32.MustStringToUTF16Ptr("WailsV0.InvokeCallback")) +} + +// initMainLoop must be called with the same OSThread that is used to call runMainLoop() later. +func (m *windowsApp) initMainLoop() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.mainThreadWindowHWND != 0 { + panic("initMainLoop was already called") + } + + // We need a hidden window so we can PostMessage to it, if we don't use PostMessage for dispatching to a HWND + // messages might get lost if a modal inner loop is being run. + // We had this once in V2: https://github.com/wailsapp/wails/issues/969 + // See: https://devblogs.microsoft.com/oldnewthing/20050426-18/?p=35783 + // See also: https://learn.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues#creating-a-message-loop + // > Because the system directs messages to individual windows in an application, a thread must create at least one window before starting its message loop. + m.mainThreadWindowHWND = w32.CreateWindowEx( + 0, + w32.MustStringToUTF16Ptr(m.parent.options.Windows.WndClass), + w32.MustStringToUTF16Ptr("__wails_hidden_mainthread"), + w32.WS_DISABLED, + w32.CW_USEDEFAULT, + w32.CW_USEDEFAULT, + 0, + 0, + 0, + 0, + w32.GetModuleHandle(""), + nil) + + m.mainThreadID, _ = w32.GetWindowThreadProcessId(m.mainThreadWindowHWND) +} + +func (m *windowsApp) runMainLoop() int { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.invokeRequired() { + panic("invokeRequired for runMainLoop, the mainloop must be running on the same OSThread as the mainThreadWindow has been created on") + } + + msg := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{}))))) + defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(msg))) + + for w32.GetMessage(msg, 0, 0, 0) != 0 { + w32.TranslateMessage(msg) + w32.DispatchMessage(msg) + } + + return int(msg.WParam) +} + +func (m *windowsApp) dispatchOnMainThread(id uint) { + mainThreadHWND := m.mainThreadWindowHWND + if mainThreadHWND == 0 { + panic("initMainLoop was not called") + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.invokeRequired() { + w32.PostMessage(mainThreadHWND, wmInvokeCallback, uintptr(id), 0) + } else { + mainThreadFunctionStoreLock.Lock() + fn := mainThreadFunctionStore[id] + delete(mainThreadFunctionStore, id) + mainThreadFunctionStoreLock.Unlock() + + if fn == nil { + Fatal("dispatchOnMainThread called with invalid id: %v", id) + } + fn() + } +} + +func (m *windowsApp) invokeRequired() bool { + mainThreadID := m.mainThreadID + if mainThreadID == 0 { + panic("initMainLoop was not called") + } + + return mainThreadID != w32.GetCurrentThreadId() +} + +func (m *windowsApp) invokeCallback(wParam, lParam uintptr) { + // TODO: Should we invoke just one or all queued? In v2 we always invoked all pendings... + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.invokeRequired() { + panic("invokeCallback must always be called on the MainOSThread") + } + + mainThreadFunctionStoreLock.Lock() + fnIDs := make([]uint, 0, len(mainThreadFunctionStore)) + for id := range mainThreadFunctionStore { + fnIDs = append(fnIDs, id) + } + sort.Slice(fnIDs, func(i, j int) bool { return fnIDs[i] < fnIDs[j] }) + + fns := make([]func(), len(fnIDs)) + for i, id := range fnIDs { + fns[i] = mainThreadFunctionStore[id] + delete(mainThreadFunctionStore, id) + } + mainThreadFunctionStoreLock.Unlock() + + for _, fn := range fns { + fn() + } +} diff --git a/v3/pkg/application/menu.go b/v3/pkg/application/menu.go new file mode 100644 index 000000000..96a971da1 --- /dev/null +++ b/v3/pkg/application/menu.go @@ -0,0 +1,233 @@ +package application + +type menuImpl interface { + update() +} + +type ContextMenu struct { + *Menu + name string +} + +func NewContextMenu(name string) *ContextMenu { + result := &ContextMenu{ + Menu: NewMenu(), + name: name, + } + result.Update() + return result +} + +func (m *ContextMenu) Update() { + m.Menu.Update() + globalApplication.ContextMenu.Add(m.name, m) +} + +func (m *ContextMenu) Destroy() { + globalApplication.ContextMenu.Remove(m.name) +} + +type Menu struct { + items []*MenuItem + label string + + impl menuImpl +} + +func NewMenu() *Menu { + return &Menu{} +} + +func (m *Menu) Add(label string) *MenuItem { + result := NewMenuItem(label) + m.items = append(m.items, result) + return result +} + +func (m *Menu) AddSeparator() { + result := NewMenuItemSeparator() + m.items = append(m.items, result) +} + +func (m *Menu) AddCheckbox(label string, enabled bool) *MenuItem { + result := NewMenuItemCheckbox(label, enabled) + m.items = append(m.items, result) + return result +} + +func (m *Menu) AddRadio(label string, enabled bool) *MenuItem { + result := NewMenuItemRadio(label, enabled) + m.items = append(m.items, result) + return result +} + +func (m *Menu) Update() { + m.processRadioGroups() + if m.impl == nil { + m.impl = newMenuImpl(m) + } + m.impl.update() +} + +// Clear all menu items +func (m *Menu) Clear() { + for _, item := range m.items { + removeMenuItemByID(item.id) + } + m.items = nil +} + +func (m *Menu) Destroy() { + for _, item := range m.items { + item.Destroy() + } + m.items = nil +} + +func (m *Menu) AddSubmenu(s string) *Menu { + result := NewSubMenuItem(s) + m.items = append(m.items, result) + return result.submenu +} + +func (m *Menu) AddRole(role Role) *Menu { + result := NewRole(role) + if result != nil { + m.items = append(m.items, result) + } + return m +} + +func (m *Menu) processRadioGroups() { + var radioGroup []*MenuItem + + closeOutRadioGroups := func() { + if len(radioGroup) > 0 { + for _, item := range radioGroup { + item.radioGroupMembers = radioGroup + } + radioGroup = []*MenuItem{} + } + } + + for _, item := range m.items { + if item.itemType != radio { + closeOutRadioGroups() + } + if item.itemType == submenu { + item.submenu.processRadioGroups() + continue + } + if item.itemType == radio { + radioGroup = append(radioGroup, item) + } + } + closeOutRadioGroups() +} + +func (m *Menu) SetLabel(label string) { + m.label = label +} + +func (m *Menu) setContextData(data *ContextMenuData) { + for _, item := range m.items { + item.setContextData(data) + } +} + +// FindByLabel recursively searches for a menu item with the given label +// and returns the first match, or nil if not found. +func (m *Menu) FindByLabel(label string) *MenuItem { + for _, item := range m.items { + if item.label == label { + return item + } + if item.submenu != nil { + found := item.submenu.FindByLabel(label) + if found != nil { + return found + } + } + } + return nil +} + +// FindByRole recursively searches for a menu item with the given role +// and returns the first match, or nil if not found. +func (m *Menu) FindByRole(role Role) *MenuItem { + for _, item := range m.items { + if item.role == role { + return item + } + if item.submenu != nil { + found := item.submenu.FindByRole(role) + if found != nil { + return found + } + } + } + return nil +} + +func (m *Menu) RemoveMenuItem(target *MenuItem) { + for i, item := range m.items { + if item == target { + // Remove the item from the slice + m.items = append(m.items[:i], m.items[i+1:]...) + break + } + if item.submenu != nil { + item.submenu.RemoveMenuItem(target) + } + } +} + +// ItemAt returns the menu item at the given index, or nil if the index is out of bounds. +func (m *Menu) ItemAt(index int) *MenuItem { + if index < 0 || index >= len(m.items) { + return nil + } + return m.items[index] +} + +// Clone recursively clones the menu and all its submenus. +func (m *Menu) Clone() *Menu { + result := &Menu{ + label: m.label, + } + for _, item := range m.items { + result.items = append(result.items, item.Clone()) + } + return result +} + +// Append menu to an existing menu +func (m *Menu) Append(in *Menu) { + if in == nil { + return + } + m.items = append(m.items, in.items...) +} + +// Prepend menu before an existing menu +func (m *Menu) Prepend(in *Menu) { + m.items = append(in.items, m.items...) +} + +func (a *App) NewMenu() *Menu { + return a.Menu.New() +} + +func NewMenuFromItems(item *MenuItem, items ...*MenuItem) *Menu { + result := &Menu{ + items: []*MenuItem{item}, + } + result.items = append(result.items, items...) + return result +} + +func NewSubmenu(s string, items *Menu) *MenuItem { + result := NewSubMenuItem(s) + result.submenu = items + return result +} diff --git a/v3/pkg/application/menu_darwin.go b/v3/pkg/application/menu_darwin.go new file mode 100644 index 000000000..a17031489 --- /dev/null +++ b/v3/pkg/application/menu_darwin.go @@ -0,0 +1,126 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.10 -x objective-c +#cgo LDFLAGS: -framework Cocoa + +#include "menuitem_darwin.h" + +extern void setMenuItemChecked(void*, unsigned int, bool); +extern void setMenuItemBitmap(void*, unsigned char*, int); + +// Clear and release all menu items in the menu +void clearMenu(void* nsMenu) { + NSMenu *menu = (NSMenu *)nsMenu; + [menu removeAllItems]; +} + + +// Create a new NSMenu +void* createNSMenu(char* label) { + NSMenu *menu = [[NSMenu alloc] init]; + if( label != NULL && strlen(label) > 0 ) { + menu.title = [NSString stringWithUTF8String:label]; + free(label); + } + [menu setAutoenablesItems:NO]; + return (void*)menu; +} + +void addMenuItem(void* nsMenu, void* nsMenuItem) { + NSMenu *menu = (NSMenu *)nsMenu; + [menu addItem:nsMenuItem]; +} + +// add seperator to menu +void addMenuSeparator(void* nsMenu) { + NSMenu *menu = (NSMenu *)nsMenu; + [menu addItem:[NSMenuItem separatorItem]]; +} + +// Set the submenu of a menu item +void setMenuItemSubmenu(void* nsMenuItem, void* nsMenu) { + NSMenuItem *menuItem = (NSMenuItem *)nsMenuItem; + NSMenu *menu = (NSMenu *)nsMenu; + [menuItem setSubmenu:menu]; +} + +// Add services menu +static void addServicesMenu(void* menu) { + NSMenu *nsMenu = (__bridge NSMenu *)menu; + [NSApp setServicesMenu:nsMenu]; +} + + +*/ +import "C" +import "unsafe" + +type macosMenu struct { + menu *Menu + + nsMenu unsafe.Pointer +} + +func newMenuImpl(menu *Menu) *macosMenu { + result := &macosMenu{ + menu: menu, + } + return result +} + +func (m *macosMenu) update() { + InvokeSync(func() { + if m.nsMenu == nil { + m.nsMenu = C.createNSMenu(C.CString(m.menu.label)) + } else { + C.clearMenu(m.nsMenu) + } + m.processMenu(m.nsMenu, m.menu) + }) +} + +func (m *macosMenu) processMenu(parent unsafe.Pointer, menu *Menu) { + for _, item := range menu.items { + switch item.itemType { + case submenu: + submenu := item.submenu + nsSubmenu := C.createNSMenu(C.CString(item.label)) + m.processMenu(nsSubmenu, submenu) + menuItem := newMenuItemImpl(item) + item.impl = menuItem + C.addMenuItem(parent, menuItem.nsMenuItem) + C.setMenuItemSubmenu(menuItem.nsMenuItem, nsSubmenu) + if item.role == ServicesMenu { + C.addServicesMenu(nsSubmenu) + } + case text, checkbox, radio: + menuItem := newMenuItemImpl(item) + item.impl = menuItem + if item.hidden { + menuItem.setHidden(true) + } + C.addMenuItem(parent, menuItem.nsMenuItem) + case separator: + C.addMenuSeparator(parent) + } + if item.bitmap != nil { + macMenuItem := item.impl.(*macosMenuItem) + C.setMenuItemBitmap(macMenuItem.nsMenuItem, (*C.uchar)(&item.bitmap[0]), C.int(len(item.bitmap))) + } + + } +} + +func DefaultApplicationMenu() *Menu { + menu := NewMenu() + menu.AddRole(AppMenu) + menu.AddRole(FileMenu) + menu.AddRole(EditMenu) + menu.AddRole(ViewMenu) + menu.AddRole(WindowMenu) + menu.AddRole(HelpMenu) + return menu +} diff --git a/v3/pkg/application/menu_linux.go b/v3/pkg/application/menu_linux.go new file mode 100644 index 000000000..b6235c2da --- /dev/null +++ b/v3/pkg/application/menu_linux.go @@ -0,0 +1,118 @@ +//go:build linux + +package application + +type linuxMenu struct { + menu *Menu + native pointer +} + +func newMenuImpl(menu *Menu) *linuxMenu { + result := &linuxMenu{ + menu: menu, + native: menuBarNew(), + } + return result +} + +func (m *linuxMenu) run() { + m.update() +} + +func (m *linuxMenu) update() { + m.processMenu(m.menu) +} + +func (m *linuxMenu) processMenu(menu *Menu) { + if menu.impl == nil { + menu.impl = &linuxMenu{ + menu: menu, + native: menuNew(), + } + } + var currentRadioGroup GSListPointer + + for _, item := range menu.items { + // drop the group if we have run out of radio items + if item.itemType != radio { + currentRadioGroup = nilRadioGroup + } + + switch item.itemType { + case submenu: + menuItem := newMenuItemImpl(item) + item.impl = menuItem + m.processMenu(item.submenu) + m.addSubMenuToItem(item.submenu, item) + m.addMenuItem(menu, item) + case text, checkbox: + menuItem := newMenuItemImpl(item) + item.impl = menuItem + m.addMenuItem(menu, item) + case radio: + menuItem := newRadioItemImpl(item, currentRadioGroup) + item.impl = menuItem + m.addMenuItem(menu, item) + currentRadioGroup = menuGetRadioGroup(menuItem) + case separator: + m.addMenuSeparator(menu) + } + + } + + for _, item := range menu.items { + if item.callback != nil { + m.attachHandler(item) + } + } + +} + +func (m *linuxMenu) attachHandler(item *MenuItem) { + (item.impl).(*linuxMenuItem).handlerId = attachMenuHandler(item) +} + +func (m *linuxMenu) addSubMenuToItem(menu *Menu, item *MenuItem) { + if menu.impl == nil { + menu.impl = &linuxMenu{ + menu: menu, + native: menuNew(), + } + } + menuSetSubmenu(item, menu) +} + +func (m *linuxMenu) addMenuItem(parent *Menu, menu *MenuItem) { + menuAppend(parent, menu) +} + +func (m *linuxMenu) addMenuSeparator(menu *Menu) { + menuAddSeparator(menu) + +} + +func (m *linuxMenu) addServicesMenu(menu *Menu) { + // FIXME: Should this be required? +} + +func (l *linuxMenu) createMenu(name string, items []*MenuItem) *Menu { + impl := newMenuImpl(&Menu{label: name}) + menu := &Menu{ + label: name, + items: items, + impl: impl, + } + impl.menu = menu + return menu +} + +func DefaultApplicationMenu() *Menu { + menu := NewMenu() + menu.AddRole(AppMenu) + menu.AddRole(FileMenu) + menu.AddRole(EditMenu) + menu.AddRole(ViewMenu) + menu.AddRole(WindowMenu) + menu.AddRole(HelpMenu) + return menu +} diff --git a/v3/pkg/application/menu_manager.go b/v3/pkg/application/menu_manager.go new file mode 100644 index 000000000..4c3a36c83 --- /dev/null +++ b/v3/pkg/application/menu_manager.go @@ -0,0 +1,55 @@ +package application + +// MenuManager manages menu-related operations +type MenuManager struct { + app *App +} + +// newMenuManager creates a new MenuManager instance +func newMenuManager(app *App) *MenuManager { + return &MenuManager{ + app: app, + } +} + +// Set sets the application menu +func (mm *MenuManager) Set(menu *Menu) { + mm.SetApplicationMenu(menu) +} + +// SetApplicationMenu sets the application menu +func (mm *MenuManager) SetApplicationMenu(menu *Menu) { + mm.app.applicationMenu = menu + if mm.app.impl != nil { + mm.app.impl.setApplicationMenu(menu) + } +} + +// GetApplicationMenu returns the current application menu +func (mm *MenuManager) GetApplicationMenu() *Menu { + return mm.app.applicationMenu +} + +// New creates a new menu +func (mm *MenuManager) New() *Menu { + return &Menu{} +} + +// ShowAbout shows the about dialog +func (mm *MenuManager) ShowAbout() { + if mm.app.impl != nil { + mm.app.impl.showAboutDialog(mm.app.options.Name, mm.app.options.Description, mm.app.options.Icon) + } +} + +// handleMenuItemClicked handles menu item click events (internal use) +func (mm *MenuManager) handleMenuItemClicked(menuItemID uint) { + defer handlePanic() + + menuItem := getMenuItemByID(menuItemID) + if menuItem == nil { + mm.app.warning("MenuItem #%d not found", menuItemID) + return + } + menuItem.handleClick() +} diff --git a/v3/pkg/application/menu_test.go b/v3/pkg/application/menu_test.go new file mode 100644 index 000000000..88aeb5724 --- /dev/null +++ b/v3/pkg/application/menu_test.go @@ -0,0 +1,140 @@ +package application_test + +import ( + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func TestMenu_FindByLabel(t *testing.T) { + tests := []struct { + name string + menu *application.Menu + label string + shouldError bool + }{ + { + name: "Find top-level item", + menu: application.NewMenuFromItems( + application.NewMenuItem("Target"), + ), + label: "Target", + shouldError: false, + }, + { + name: "Find item in submenu", + menu: application.NewMenuFromItems( + application.NewMenuItem("Item 1"), + application.NewSubmenu("Submenu", application.NewMenuFromItems( + application.NewMenuItem("Subitem 1"), + application.NewMenuItem("Target"), + )), + ), + label: "Target", + shouldError: false, + }, + { + name: "Not find item", + menu: application.NewMenuFromItems( + application.NewMenuItem("Item 1"), + application.NewSubmenu("Submenu", application.NewMenuFromItems( + application.NewMenuItem("Subitem 1"), + application.NewMenuItem("Target"), + )), + ), + label: "Random", + shouldError: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + found := test.menu.FindByLabel(test.label) + if test.shouldError && found != nil { + t.Errorf("Expected error, but found %v", found) + } + if !test.shouldError && found == nil { + t.Errorf("Expected item, but found none") + } + }) + } +} + +func TestMenu_ItemAt(t *testing.T) { + tests := []struct { + name string + menu *application.Menu + index int + shouldError bool + }{ + { + name: "Valid index", + menu: application.NewMenuFromItems( + application.NewMenuItem("Item 1"), + application.NewMenuItem("Item 2"), + application.NewMenuItem("Target"), + ), + index: 2, + shouldError: false, + }, + { + name: "Index out of bounds (negative)", + menu: application.NewMenuFromItems( + application.NewMenuItem("Item 1"), + application.NewMenuItem("Item 2"), + ), + index: -1, + shouldError: true, + }, + { + name: "Index out of bounds (too large)", + menu: application.NewMenuFromItems( + application.NewMenuItem("Item 1"), + application.NewMenuItem("Item 2"), + ), + index: 2, + shouldError: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + item := test.menu.ItemAt(test.index) + if test.shouldError && item != nil { + t.Errorf("Expected error, but found %v", item) + } + if !test.shouldError && item == nil { + t.Errorf("Expected item, but found none") + } + }) + } +} + +func TestMenu_RemoveMenuItem(t *testing.T) { + itemToRemove := application.NewMenuItem("Target") + itemToKeep := application.NewMenuItem("Item 1") + + tests := []struct { + name string + menu *application.Menu + item *application.MenuItem + shouldFind bool + }{ + { + name: "Remove existing item", + menu: application.NewMenuFromItems(itemToKeep, itemToRemove), + item: itemToRemove, + shouldFind: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.menu.RemoveMenuItem(test.item) + found := test.menu.FindByLabel(test.item.Label()) + if !test.shouldFind && found != nil { + t.Errorf("Expected item to be removed, but found %v", found) + } + }) + } +} diff --git a/v3/pkg/application/menu_windows.go b/v3/pkg/application/menu_windows.go new file mode 100644 index 000000000..210d22c0d --- /dev/null +++ b/v3/pkg/application/menu_windows.go @@ -0,0 +1,142 @@ +//go:build windows + +package application + +import ( + "github.com/wailsapp/wails/v3/pkg/w32" +) + +type windowsMenu struct { + menu *Menu + parentWindow *windowsWebviewWindow + + hWnd w32.HWND + hMenu w32.HMENU + currentMenuID int + menuMapping map[int]*MenuItem + checkboxItems []*Menu +} + +func newMenuImpl(menu *Menu) *windowsMenu { + result := &windowsMenu{ + menu: menu, + menuMapping: make(map[int]*MenuItem), + } + + return result +} + +func (w *windowsMenu) update() { + if w.hMenu != 0 { + w32.DestroyMenu(w.hMenu) + } + w.hMenu = w32.NewPopupMenu() + w.processMenu(w.hMenu, w.menu) +} + +func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) { + for _, item := range inputMenu.items { + w.currentMenuID++ + itemID := w.currentMenuID + w.menuMapping[itemID] = item + + menuItemImpl := newMenuItemImpl(item, parentMenu, itemID) + menuItemImpl.parent = inputMenu + item.impl = menuItemImpl + + if item.Hidden() { + if item.accelerator != nil && item.callback != nil { + if w.parentWindow != nil { + w.parentWindow.parent.removeMenuBinding(item.accelerator) + } else { + globalApplication.KeyBinding.Remove(item.accelerator.String()) + } + } + } + + flags := uint32(w32.MF_STRING) + if item.disabled { + flags = flags | w32.MF_GRAYED + } + if item.checked { + flags = flags | w32.MF_CHECKED + } + if item.IsSeparator() { + flags = flags | w32.MF_SEPARATOR + } + if item.itemType == radio { + flags = flags | w32.MFT_RADIOCHECK + } + + if item.submenu != nil { + flags = flags | w32.MF_POPUP + newSubmenu := w32.CreateMenu() + w.processMenu(newSubmenu, item.submenu) + itemID = int(newSubmenu) + } + + thisText := item.Label() + if item.accelerator != nil && item.callback != nil { + if w.parentWindow != nil { + w.parentWindow.parent.addMenuBinding(item.accelerator, item) + } else { + globalApplication.KeyBinding.Add(item.accelerator.String(), func(w Window) { + item.handleClick() + }) + } + thisText = thisText + "\t" + item.accelerator.String() + } + var menuText = w32.MustStringToUTF16Ptr(thisText) + + // If the item is hidden, don't append + if item.Hidden() { + continue + } + + w32.AppendMenu(parentMenu, flags, uintptr(itemID), menuText) + if item.bitmap != nil { + if err := w32.SetMenuIcons(parentMenu, itemID, item.bitmap, nil); err != nil { + globalApplication.fatal("error setting menu icons: %w", err) + } + } + } +} + +func (w *windowsMenu) ShowAtCursor() { + InvokeSync(func() { + x, y, ok := w32.GetCursorPos() + if !ok { + return + } + w.ShowAt(x, y) + }) +} + +func (w *windowsMenu) ShowAt(x int, y int) { + w.update() + w32.TrackPopupMenuEx(w.hMenu, + w32.TPM_LEFTALIGN, + int32(x), + int32(y), + w.hWnd, + nil) + w32.PostMessage(w.hWnd, w32.WM_NULL, 0, 0) +} + +func (w *windowsMenu) ProcessCommand(cmdMsgID int) { + item := w.menuMapping[cmdMsgID] + if item == nil { + return + } + item.handleClick() +} + +func DefaultApplicationMenu() *Menu { + menu := NewMenu() + menu.AddRole(FileMenu) + menu.AddRole(EditMenu) + menu.AddRole(ViewMenu) + menu.AddRole(WindowMenu) + menu.AddRole(HelpMenu) + return menu +} diff --git a/v3/pkg/application/menuitem.go b/v3/pkg/application/menuitem.go new file mode 100644 index 000000000..5d11a87c7 --- /dev/null +++ b/v3/pkg/application/menuitem.go @@ -0,0 +1,456 @@ +package application + +import ( + "sync" + "sync/atomic" +) + +type menuItemType int + +const ( + text menuItemType = iota + separator + checkbox + radio + submenu +) + +var menuItemID uintptr +var menuItemMap = make(map[uint]*MenuItem) +var menuItemMapLock sync.Mutex + +func addToMenuItemMap(menuItem *MenuItem) { + menuItemMapLock.Lock() + menuItemMap[menuItem.id] = menuItem + menuItemMapLock.Unlock() +} + +func getMenuItemByID(id uint) *MenuItem { + menuItemMapLock.Lock() + defer menuItemMapLock.Unlock() + return menuItemMap[id] +} + +func removeMenuItemByID(id uint) { + menuItemMapLock.Lock() + defer menuItemMapLock.Unlock() + delete(menuItemMap, id) +} + +type menuItemImpl interface { + setTooltip(s string) + setLabel(s string) + setDisabled(disabled bool) + setChecked(checked bool) + setAccelerator(accelerator *accelerator) + setHidden(hidden bool) + setBitmap(bitmap []byte) + destroy() +} + +type MenuItem struct { + id uint + label string + tooltip string + disabled bool + checked bool + hidden bool + bitmap []byte + submenu *Menu + callback func(*Context) + itemType menuItemType + accelerator *accelerator + role Role + contextMenuData *ContextMenuData + + impl menuItemImpl + radioGroupMembers []*MenuItem +} + +func NewMenuItem(label string) *MenuItem { + result := &MenuItem{ + id: uint(atomic.AddUintptr(&menuItemID, 1)), + label: label, + itemType: text, + } + addToMenuItemMap(result) + return result +} + +func NewMenuItemSeparator() *MenuItem { + result := &MenuItem{ + id: uint(atomic.AddUintptr(&menuItemID, 1)), + itemType: separator, + } + return result +} + +func NewMenuItemCheckbox(label string, checked bool) *MenuItem { + result := &MenuItem{ + id: uint(atomic.AddUintptr(&menuItemID, 1)), + label: label, + checked: checked, + itemType: checkbox, + } + addToMenuItemMap(result) + return result +} + +func NewMenuItemRadio(label string, checked bool) *MenuItem { + result := &MenuItem{ + id: uint(atomic.AddUintptr(&menuItemID, 1)), + label: label, + checked: checked, + itemType: radio, + } + addToMenuItemMap(result) + return result +} + +func NewSubMenuItem(label string) *MenuItem { + result := &MenuItem{ + id: uint(atomic.AddUintptr(&menuItemID, 1)), + label: label, + itemType: submenu, + submenu: &Menu{ + label: label, + }, + } + addToMenuItemMap(result) + return result +} + +func NewRole(role Role) *MenuItem { + var result *MenuItem + switch role { + case AppMenu: + result = NewAppMenu() + case EditMenu: + result = NewEditMenu() + case FileMenu: + result = NewFileMenu() + case ViewMenu: + result = NewViewMenu() + case ServicesMenu: + return NewServicesMenu() + case SpeechMenu: + result = NewSpeechMenu() + case WindowMenu: + result = NewWindowMenu() + case HelpMenu: + result = NewHelpMenu() + case Hide: + result = NewHideMenuItem() + case Front: + result = NewFrontMenuItem() + case HideOthers: + result = NewHideOthersMenuItem() + case UnHide: + result = NewUnhideMenuItem() + case Undo: + result = NewUndoMenuItem() + case Redo: + result = NewRedoMenuItem() + case Cut: + result = NewCutMenuItem() + case Copy: + result = NewCopyMenuItem() + case Paste: + result = NewPasteMenuItem() + case PasteAndMatchStyle: + result = NewPasteAndMatchStyleMenuItem() + case SelectAll: + result = NewSelectAllMenuItem() + case Delete: + result = NewDeleteMenuItem() + case Quit: + result = NewQuitMenuItem() + case CloseWindow: + result = NewCloseMenuItem() + case About: + result = NewAboutMenuItem() + case Reload: + result = NewReloadMenuItem() + case ForceReload: + result = NewForceReloadMenuItem() + case ToggleFullscreen: + result = NewToggleFullscreenMenuItem() + case OpenDevTools: + result = NewOpenDevToolsMenuItem() + case ResetZoom: + result = NewZoomResetMenuItem() + case ZoomIn: + result = NewZoomInMenuItem() + case ZoomOut: + result = NewZoomOutMenuItem() + case Minimise: + result = NewMinimiseMenuItem() + case Zoom: + result = NewZoomMenuItem() + case FullScreen: + result = NewFullScreenMenuItem() + case Print: + result = NewPrintMenuItem() + case PageLayout: + result = NewPageLayoutMenuItem() + case NoRole: + case ShowAll: + result = NewShowAllMenuItem() + case BringAllToFront: + result = NewBringAllToFrontMenuItem() + case NewFile: + result = NewNewFileMenuItem() + case Open: + result = NewOpenMenuItem() + case Save: + result = NewSaveMenuItem() + case SaveAs: + result = NewSaveAsMenuItem() + case StartSpeaking: + result = NewStartSpeakingMenuItem() + case StopSpeaking: + result = NewStopSpeakingMenuItem() + case Revert: + result = NewRevertMenuItem() + case Find: + result = NewFindMenuItem() + case FindAndReplace: + result = NewFindAndReplaceMenuItem() + case FindNext: + result = NewFindNextMenuItem() + case FindPrevious: + result = NewFindPreviousMenuItem() + case Help: + result = NewHelpMenuItem() + + default: + globalApplication.error("no support for role: %v", role) + } + + if result != nil { + result.role = role + } + + return result +} + +func NewServicesMenu() *MenuItem { + serviceMenu := NewSubMenuItem("Services") + serviceMenu.role = ServicesMenu + return serviceMenu +} + +func (m *MenuItem) handleClick() { + var ctx = newContext(). + withClickedMenuItem(m). + withContextMenuData(m.contextMenuData) + if m.itemType == checkbox { + m.checked = !m.checked + ctx.withChecked(m.checked) + if m.impl != nil { + m.impl.setChecked(m.checked) + } + } + if m.itemType == radio { + for _, member := range m.radioGroupMembers { + member.checked = false + if member.impl != nil { + member.impl.setChecked(false) + } + } + m.checked = true + ctx.withChecked(true) + if m.impl != nil { + m.impl.setChecked(true) + } + } + if m.callback != nil { + go func() { + defer handlePanic() + m.callback(ctx) + }() + } +} + +func (m *MenuItem) SetAccelerator(shortcut string) *MenuItem { + accelerator, err := parseAccelerator(shortcut) + if err != nil { + globalApplication.error("invalid accelerator: %w", err) + return m + } + m.accelerator = accelerator + if m.impl != nil { + m.impl.setAccelerator(accelerator) + } + return m +} + +func (m *MenuItem) GetAccelerator() string { + if m.accelerator == nil { + return "" + } + return m.accelerator.String() +} + +func (m *MenuItem) RemoveAccelerator() { + m.accelerator = nil +} + +func (m *MenuItem) SetTooltip(s string) *MenuItem { + m.tooltip = s + if m.impl != nil { + m.impl.setTooltip(s) + } + return m +} + +func (m *MenuItem) SetRole(role Role) *MenuItem { + m.role = role + return m +} + +func (m *MenuItem) SetLabel(s string) *MenuItem { + m.label = s + if m.impl != nil { + m.impl.setLabel(s) + } + return m +} + +func (m *MenuItem) SetEnabled(enabled bool) *MenuItem { + m.disabled = !enabled + if m.impl != nil { + m.impl.setDisabled(m.disabled) + } + return m +} + +func (m *MenuItem) SetBitmap(bitmap []byte) *MenuItem { + m.bitmap = bitmap + if m.impl != nil { + m.impl.setBitmap(bitmap) + } + return m +} + +func (m *MenuItem) SetChecked(checked bool) *MenuItem { + m.checked = checked + if m.impl != nil { + m.impl.setChecked(m.checked) + } + return m +} + +func (m *MenuItem) SetHidden(hidden bool) *MenuItem { + m.hidden = hidden + if m.impl != nil { + m.impl.setHidden(m.hidden) + } + return m +} + +// GetSubmenu returns the submenu of the MenuItem. +// If the MenuItem is not a submenu, it returns nil. +func (m *MenuItem) GetSubmenu() *Menu { + return m.submenu +} + +func (m *MenuItem) Checked() bool { + return m.checked +} + +func (m *MenuItem) IsSeparator() bool { + return m.itemType == separator +} + +func (m *MenuItem) IsSubmenu() bool { + return m.itemType == submenu +} + +func (m *MenuItem) IsCheckbox() bool { + return m.itemType == checkbox +} + +func (m *MenuItem) IsRadio() bool { + return m.itemType == radio +} + +func (m *MenuItem) Hidden() bool { + return m.hidden +} + +func (m *MenuItem) OnClick(f func(*Context)) *MenuItem { + m.callback = f + return m +} + +func (m *MenuItem) Label() string { + return m.label +} + +func (m *MenuItem) Tooltip() string { + return m.tooltip +} + +func (m *MenuItem) Enabled() bool { + return !m.disabled +} + +func (m *MenuItem) setContextData(data *ContextMenuData) { + m.contextMenuData = data + if m.submenu != nil { + m.submenu.setContextData(data) + } +} + +// Clone returns a deep copy of the MenuItem +func (m *MenuItem) Clone() *MenuItem { + result := &MenuItem{ + id: m.id, + label: m.label, + tooltip: m.tooltip, + disabled: m.disabled, + checked: m.checked, + hidden: m.hidden, + bitmap: m.bitmap, + callback: m.callback, + itemType: m.itemType, + role: m.role, + } + if m.submenu != nil { + result.submenu = m.submenu.Clone() + } + if m.accelerator != nil { + result.accelerator = m.accelerator.clone() + } + if m.contextMenuData != nil { + result.contextMenuData = m.contextMenuData.clone() + } + return result +} + +func (m *MenuItem) Destroy() { + + removeMenuItemByID(m.id) + + // Clean up resources + if m.impl != nil { + m.impl.destroy() + } + if m.submenu != nil { + m.submenu.Destroy() + m.submenu = nil + } + + if m.contextMenuData != nil { + m.contextMenuData = nil + } + + if m.accelerator != nil { + m.accelerator = nil + } + + m.callback = nil + m.radioGroupMembers = nil + +} diff --git a/v3/pkg/application/menuitem_darwin.go b/v3/pkg/application/menuitem_darwin.go new file mode 100644 index 000000000..0ba1de8b2 --- /dev/null +++ b/v3/pkg/application/menuitem_darwin.go @@ -0,0 +1,388 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa -framework WebKit + +#include "Cocoa/Cocoa.h" +#include "menuitem_darwin.h" +#include "application_darwin.h" + +#define unicode(input) [NSString stringWithFormat:@"%C", input] + +// Create menu item +void* newMenuItem(unsigned int menuItemID, char *label, bool disabled, char* tooltip, char* selector) { + MenuItem *menuItem = [MenuItem new]; + + // Label + menuItem.title = [NSString stringWithUTF8String:label]; + + // Always set the action regardless of disabled state + if (selector != NULL) { + menuItem.action = NSSelectorFromString([NSString stringWithUTF8String:selector]); + menuItem.target = disabled ? nil : nil; // Role-based actions always use responder chain + } else { + menuItem.action = @selector(handleClick); + menuItem.target = disabled ? nil : menuItem; // Custom callbacks need target=menuItem when enabled + } + menuItem.menuItemID = menuItemID; + + menuItem.enabled = !disabled; + + // Tooltip + if( tooltip != NULL ) { + menuItem.toolTip = [NSString stringWithUTF8String:tooltip]; + free(tooltip); + } + + // Set the tag + [menuItem setTag:menuItemID]; + + return (void*)menuItem; +} + +// set menu item label +void setMenuItemLabel(void* nsMenuItem, char *label) { + dispatch_async(dispatch_get_main_queue(), ^{ + MenuItem *menuItem = (MenuItem *)nsMenuItem; + menuItem.title = [NSString stringWithUTF8String:label]; + free(label); + }); +} + +// set menu item disabled +void setMenuItemDisabled(void* nsMenuItem, bool disabled) { + dispatch_async(dispatch_get_main_queue(), ^{ + MenuItem *menuItem = (MenuItem *)nsMenuItem; + [menuItem setEnabled:!disabled]; + // Handle target based on whether item uses custom selector or handleClick + if( disabled ) { + [menuItem setTarget:nil]; + } else { + // Check if this menu item uses a custom selector (role-based) + // by checking if the action is handleClick or something else + if ([menuItem action] == @selector(handleClick)) { + // This is a custom callback menu item, set target to self + [menuItem setTarget:menuItem]; + } else { + // This is a role-based menu item, target should be nil + // to allow the action to be sent up the responder chain + [menuItem setTarget:nil]; + } + } + }); +} + +// set menu item hidden +void setMenuItemHidden(void* nsMenuItem, bool hidden) { + dispatch_async(dispatch_get_main_queue(), ^{ + MenuItem *menuItem = (MenuItem *)nsMenuItem; + [menuItem setHidden:hidden]; + }); +} + +// set menu item tooltip +void setMenuItemTooltip(void* nsMenuItem, char *tooltip) { + dispatch_async(dispatch_get_main_queue(), ^{ + MenuItem *menuItem = (MenuItem *)nsMenuItem; + menuItem.toolTip = [NSString stringWithUTF8String:tooltip]; + free(tooltip); + }); +} + +// Check menu item +void setMenuItemChecked(void* nsMenuItem, bool checked) { + dispatch_async(dispatch_get_main_queue(), ^{ + MenuItem *menuItem = (MenuItem *)nsMenuItem; + menuItem.state = checked ? NSControlStateValueOn : NSControlStateValueOff; + }); +} + +NSString* translateKey(NSString* key) { + + // Guard against no accelerator key + if( key == NULL ) { + return @""; + } + + if( [key isEqualToString:@"backspace"] ) { + return unicode(0x0008); + } + if( [key isEqualToString:@"tab"] ) { + return unicode(0x0009); + } + if( [key isEqualToString:@"return"] ) { + return unicode(0x000d); + } + if( [key isEqualToString:@"enter"] ) { + return unicode(0x000d); + } + if( [key isEqualToString:@"escape"] ) { + return unicode(0x001b); + } + if( [key isEqualToString:@"left"] ) { + return unicode(0xf702); + } + if( [key isEqualToString:@"right"] ) { + return unicode(0xf703); + } + if( [key isEqualToString:@"up"] ) { + return unicode(0xf700); + } + if( [key isEqualToString:@"down"] ) { + return unicode(0xf701); + } + if( [key isEqualToString:@"space"] ) { + return unicode(0x0020); + } + if( [key isEqualToString:@"delete"] ) { + return unicode(0x007f); + } + if( [key isEqualToString:@"home"] ) { + return unicode(0x2196); + } + if( [key isEqualToString:@"end"] ) { + return unicode(0x2198); + } + if( [key isEqualToString:@"page up"] ) { + return unicode(0x21de); + } + if( [key isEqualToString:@"page down"] ) { + return unicode(0x21df); + } + if( [key isEqualToString:@"f1"] ) { + return unicode(0xf704); + } + if( [key isEqualToString:@"f2"] ) { + return unicode(0xf705); + } + if( [key isEqualToString:@"f3"] ) { + return unicode(0xf706); + } + if( [key isEqualToString:@"f4"] ) { + return unicode(0xf707); + } + if( [key isEqualToString:@"f5"] ) { + return unicode(0xf708); + } + if( [key isEqualToString:@"f6"] ) { + return unicode(0xf709); + } + if( [key isEqualToString:@"f7"] ) { + return unicode(0xf70a); + } + if( [key isEqualToString:@"f8"] ) { + return unicode(0xf70b); + } + if( [key isEqualToString:@"f9"] ) { + return unicode(0xf70c); + } + if( [key isEqualToString:@"f10"] ) { + return unicode(0xf70d); + } + if( [key isEqualToString:@"f11"] ) { + return unicode(0xf70e); + } + if( [key isEqualToString:@"f12"] ) { + return unicode(0xf70f); + } + if( [key isEqualToString:@"f13"] ) { + return unicode(0xf710); + } + if( [key isEqualToString:@"f14"] ) { + return unicode(0xf711); + } + if( [key isEqualToString:@"f15"] ) { + return unicode(0xf712); + } + if( [key isEqualToString:@"f16"] ) { + return unicode(0xf713); + } + if( [key isEqualToString:@"f17"] ) { + return unicode(0xf714); + } + if( [key isEqualToString:@"f18"] ) { + return unicode(0xf715); + } + if( [key isEqualToString:@"f19"] ) { + return unicode(0xf716); + } + if( [key isEqualToString:@"f20"] ) { + return unicode(0xf717); + } + if( [key isEqualToString:@"f21"] ) { + return unicode(0xf718); + } + if( [key isEqualToString:@"f22"] ) { + return unicode(0xf719); + } + if( [key isEqualToString:@"f23"] ) { + return unicode(0xf71a); + } + if( [key isEqualToString:@"f24"] ) { + return unicode(0xf71b); + } + if( [key isEqualToString:@"f25"] ) { + return unicode(0xf71c); + } + if( [key isEqualToString:@"f26"] ) { + return unicode(0xf71d); + } + if( [key isEqualToString:@"f27"] ) { + return unicode(0xf71e); + } + if( [key isEqualToString:@"f28"] ) { + return unicode(0xf71f); + } + if( [key isEqualToString:@"f29"] ) { + return unicode(0xf720); + } + if( [key isEqualToString:@"f30"] ) { + return unicode(0xf721); + } + if( [key isEqualToString:@"f31"] ) { + return unicode(0xf722); + } + if( [key isEqualToString:@"f32"] ) { + return unicode(0xf723); + } + if( [key isEqualToString:@"f33"] ) { + return unicode(0xf724); + } + if( [key isEqualToString:@"f34"] ) { + return unicode(0xf725); + } + if( [key isEqualToString:@"f35"] ) { + return unicode(0xf726); + } + if( [key isEqualToString:@"numLock"] ) { + return unicode(0xf739); + } + return key; +} + +// Set the menuitem key equivalent +void setMenuItemKeyEquivalent(void* nsMenuItem, char *key, int modifier) { + MenuItem *menuItem = (MenuItem *)nsMenuItem; + NSString *nskey = [NSString stringWithUTF8String:key]; + menuItem.keyEquivalent = translateKey(nskey); + menuItem.keyEquivalentModifierMask = modifier; + free(key); +} + +// Call the copy selector on the pasteboard +static void copyToPasteboard(char *text) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard clearContents]; + [pasteboard setString:[NSString stringWithUTF8String:text] forType:NSPasteboardTypeString]; +} + +// Call the paste selector on the pasteboard +static char *pasteFromPasteboard(void) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSString *text = [pasteboard stringForType:NSPasteboardTypeString]; + if( text == nil ) { + return NULL; + } + return strdup([text UTF8String]); +} + +void performSelectorOnMainThreadForFirstResponder(SEL selector) { + NSWindow *activeWindow = [[NSApplication sharedApplication] keyWindow]; + if (activeWindow) { + [activeWindow performSelectorOnMainThread:selector withObject:nil waitUntilDone:YES]; + } +} + +void setMenuItemBitmap(void* nsMenuItem, unsigned char *bitmap, int length) { + MenuItem *menuItem = (MenuItem *)nsMenuItem; + NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithBytes:bitmap length:length]]; + [menuItem setImage:image]; +} + +void destroyMenuItem(void* nsMenuItem) { + MenuItem *menuItem = (MenuItem *)nsMenuItem; + [menuItem release]; +} +*/ +import "C" +import ( + "unsafe" +) + +type macosMenuItem struct { + menuItem *MenuItem + + nsMenuItem unsafe.Pointer +} + +func (m macosMenuItem) setTooltip(tooltip string) { + C.setMenuItemTooltip(m.nsMenuItem, C.CString(tooltip)) +} + +func (m macosMenuItem) setLabel(s string) { + C.setMenuItemLabel(m.nsMenuItem, C.CString(s)) +} + +func (m macosMenuItem) setDisabled(disabled bool) { + C.setMenuItemDisabled(m.nsMenuItem, C.bool(disabled)) +} + +func (m macosMenuItem) setChecked(checked bool) { + C.setMenuItemChecked(m.nsMenuItem, C.bool(checked)) +} + +func (m macosMenuItem) setHidden(hidden bool) { + C.setMenuItemHidden(m.nsMenuItem, C.bool(hidden)) +} + +func (m macosMenuItem) setBitmap(bitmap []byte) { + C.setMenuItemBitmap(m.nsMenuItem, (*C.uchar)(&bitmap[0]), C.int(len(bitmap))) +} + +func (m macosMenuItem) setAccelerator(accelerator *accelerator) { + // Set the keyboard shortcut of the menu item + var modifier C.int + var key *C.char + if accelerator != nil { + modifier = C.int(toMacModifier(accelerator.Modifiers)) + key = C.CString(accelerator.Key) + } + + // Convert the key to a string + C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier) +} + +func (m macosMenuItem) destroy() { + C.destroyMenuItem(m.nsMenuItem) +} + +func newMenuItemImpl(item *MenuItem) *macosMenuItem { + result := &macosMenuItem{ + menuItem: item, + } + + selector := getSelectorForRole(item.role) + if selector != nil { + defer C.free(unsafe.Pointer(selector)) + } + result.nsMenuItem = unsafe.Pointer(C.newMenuItem( + C.uint(item.id), + C.CString(item.label), + C.bool(item.disabled), + C.CString(item.tooltip), + selector, + )) + + switch item.itemType { + case checkbox, radio: + C.setMenuItemChecked(result.nsMenuItem, C.bool(item.checked)) + } + + if item.accelerator != nil { + result.setAccelerator(item.accelerator) + } + return result +} diff --git a/v3/pkg/application/menuitem_darwin.h b/v3/pkg/application/menuitem_darwin.h new file mode 100644 index 000000000..8260d4dfd --- /dev/null +++ b/v3/pkg/application/menuitem_darwin.h @@ -0,0 +1,17 @@ +#ifndef MenuItemDelegate_h +#define MenuItemDelegate_h + +#import + +extern void processMenuItemClick(unsigned int); + +@interface MenuItem : NSMenuItem + +@property unsigned int menuItemID; + +- (void) handleClick; + +@end + + +#endif /* MenuItemDelegate_h */ diff --git a/v3/pkg/application/menuitem_darwin.m b/v3/pkg/application/menuitem_darwin.m new file mode 100644 index 000000000..39369502d --- /dev/null +++ b/v3/pkg/application/menuitem_darwin.m @@ -0,0 +1,13 @@ +//go:build darwin + +#import + +#import "menuitem_darwin.h" + +@implementation MenuItem + +- (void) handleClick { + processMenuItemClick(self.menuItemID); +} + +@end diff --git a/v3/pkg/application/menuitem_dev.go b/v3/pkg/application/menuitem_dev.go new file mode 100644 index 000000000..4ce99fd66 --- /dev/null +++ b/v3/pkg/application/menuitem_dev.go @@ -0,0 +1,14 @@ +//go:build !production || devtools + +package application + +func NewOpenDevToolsMenuItem() *MenuItem { + return NewMenuItem("Open Developer Tools"). + SetAccelerator("Alt+Command+I"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.OpenDevTools() + } + }) +} diff --git a/v3/pkg/application/menuitem_linux.go b/v3/pkg/application/menuitem_linux.go new file mode 100644 index 000000000..68a3ddd4a --- /dev/null +++ b/v3/pkg/application/menuitem_linux.go @@ -0,0 +1,436 @@ +//go:build linux + +package application + +import ( + "fmt" + "runtime" +) + +type linuxMenuItem struct { + menuItem *MenuItem + native pointer + handlerId uint +} + +func (l linuxMenuItem) setTooltip(tooltip string) { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + menuItemSetToolTip(l.native, tooltip) + }) +} + +func (l linuxMenuItem) destroy() { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + menuItemDestroy(l.native) + }) +} + +func (l linuxMenuItem) blockSignal() { + if l.handlerId != 0 { + menuItemSignalBlock(l.native, l.handlerId, true) + } +} +func (l linuxMenuItem) setBitmap(data []byte) { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + menuItemSetBitmap(l.native, data) + }) +} + +func (l linuxMenuItem) unBlockSignal() { + if l.handlerId != 0 { + menuItemSignalBlock(l.native, l.handlerId, false) + } +} + +func (l linuxMenuItem) setLabel(s string) { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + menuItemSetLabel(l.native, s) + }) +} + +func (l linuxMenuItem) isChecked() bool { + return menuItemChecked(l.native) +} + +func (l linuxMenuItem) setDisabled(disabled bool) { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + menuItemSetDisabled(l.native, disabled) + }) +} + +func (l linuxMenuItem) setChecked(checked bool) { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + menuItemSetChecked(l.native, checked) + }) +} + +func (l linuxMenuItem) setHidden(hidden bool) { + InvokeSync(func() { + l.blockSignal() + defer l.unBlockSignal() + widgetSetVisible(l.native, hidden) + }) +} + +func (l linuxMenuItem) setAccelerator(accelerator *accelerator) { + fmt.Println("setAccelerator", accelerator) + // Set the keyboard shortcut of the menu item + // var modifier C.int + // var key *C.char + if accelerator != nil { + // modifier = C.int(toMacModifier(accelerator.Modifiers)) + // key = C.CString(accelerator.Key) + } + + // Convert the key to a string + // C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier) +} + +func newMenuItemImpl(item *MenuItem) *linuxMenuItem { + result := &linuxMenuItem{ + menuItem: item, + } + switch item.itemType { + case text: + result.native = menuItemNew(item.label, item.bitmap) + + case checkbox: + result.native = menuCheckItemNew(item.label, item.bitmap) + result.setChecked(item.checked) + if item.accelerator != nil { + result.setAccelerator(item.accelerator) + } + case submenu: + result.native = menuItemNew(item.label, item.bitmap) + + default: + panic(fmt.Sprintf("Unknown menu type: %v", item.itemType)) + } + result.setDisabled(result.menuItem.disabled) + return result +} + +func newRadioItemImpl(item *MenuItem, group GSListPointer) *linuxMenuItem { + result := &linuxMenuItem{ + menuItem: item, + native: menuRadioItemNew(group, item.label), + } + result.setChecked(item.checked) + result.setDisabled(result.menuItem.disabled) + return result +} + +func newSpeechMenu() *MenuItem { + speechMenu := NewMenu() + speechMenu.Add("Start Speaking"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+."). + OnClick(func(ctx *Context) { + // C.startSpeaking() + }) + speechMenu.Add("Stop Speaking"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+,"). + OnClick(func(ctx *Context) { + // C.stopSpeaking() + }) + subMenu := NewSubMenuItem("Speech") + subMenu.submenu = speechMenu + return subMenu +} + +func newFrontMenuItem() *MenuItem { + panic("implement me") +} + +func newHideMenuItem() *MenuItem { + return NewMenuItem("Hide " + globalApplication.options.Name). + SetAccelerator("CmdOrCtrl+h"). + OnClick(func(ctx *Context) { + + // C.hideApplication() + }) +} + +func newHideOthersMenuItem() *MenuItem { + return NewMenuItem("Hide Others"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+h"). + OnClick(func(ctx *Context) { + // C.hideOthers() + }) +} + +func newUnhideMenuItem() *MenuItem { + return NewMenuItem("Show All"). + OnClick(func(ctx *Context) { + // C.showAll() + }) +} + +func newUndoMenuItem() *MenuItem { + return NewMenuItem("Undo"). + SetAccelerator("CmdOrCtrl+z"). + OnClick(func(ctx *Context) { + // C.undo() + }) +} + +// newRedoMenuItem creates a new menu item for redoing the last action +func newRedoMenuItem() *MenuItem { + return NewMenuItem("Redo"). + SetAccelerator("CmdOrCtrl+Shift+z"). + OnClick(func(ctx *Context) { + // C.redo() + }) +} + +func newCutMenuItem() *MenuItem { + return NewMenuItem("Cut"). + SetAccelerator("CmdOrCtrl+x"). + OnClick(func(ctx *Context) { + // C.cut() + }) +} + +func newCopyMenuItem() *MenuItem { + return NewMenuItem("Copy"). + SetAccelerator("CmdOrCtrl+c"). + OnClick(func(ctx *Context) { + // C.copy() + }) +} + +func newPasteMenuItem() *MenuItem { + return NewMenuItem("Paste"). + SetAccelerator("CmdOrCtrl+v"). + OnClick(func(ctx *Context) { + // C.paste() + }) +} + +func newPasteAndMatchStyleMenuItem() *MenuItem { + return NewMenuItem("Paste and Match Style"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+v"). + OnClick(func(ctx *Context) { + // C.pasteAndMatchStyle() + }) +} + +func newDeleteMenuItem() *MenuItem { + return NewMenuItem("Delete"). + SetAccelerator("backspace"). + OnClick(func(ctx *Context) { + // C.delete() + }) +} + +func newQuitMenuItem() *MenuItem { + return NewMenuItem("Quit " + globalApplication.options.Name). + SetAccelerator("CmdOrCtrl+q"). + OnClick(func(ctx *Context) { + globalApplication.Quit() + }) +} + +func newSelectAllMenuItem() *MenuItem { + return NewMenuItem("Select All"). + SetAccelerator("CmdOrCtrl+a"). + OnClick(func(ctx *Context) { + // C.selectAll() + }) +} + +func newAboutMenuItem() *MenuItem { + return NewMenuItem("About " + globalApplication.options.Name). + OnClick(func(ctx *Context) { + globalApplication.Menu.ShowAbout() + }) +} + +func newCloseMenuItem() *MenuItem { + return NewMenuItem("Close"). + SetAccelerator("CmdOrCtrl+w"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Close() + } + }) +} + +func newReloadMenuItem() *MenuItem { + return NewMenuItem("Reload"). + SetAccelerator("CmdOrCtrl+r"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Reload() + } + }) +} + +func newForceReloadMenuItem() *MenuItem { + return NewMenuItem("Force Reload"). + SetAccelerator("CmdOrCtrl+Shift+r"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ForceReload() + } + }) +} + +func newToggleFullscreenMenuItem() *MenuItem { + result := NewMenuItem("Toggle Full Screen"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ToggleFullscreen() + } + }) + if runtime.GOOS == "darwin" { + result.SetAccelerator("Ctrl+Command+F") + } else { + result.SetAccelerator("F11") + } + return result +} + +func newZoomResetMenuItem() *MenuItem { + // reset zoom menu item + return NewMenuItem("Actual Size"). + SetAccelerator("CmdOrCtrl+0"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ZoomReset() + } + }) +} + +func newZoomInMenuItem() *MenuItem { + return NewMenuItem("Zoom In"). + SetAccelerator("CmdOrCtrl+plus"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ZoomIn() + } + }) +} + +func newZoomOutMenuItem() *MenuItem { + return NewMenuItem("Zoom Out"). + SetAccelerator("CmdOrCtrl+-"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ZoomOut() + } + }) +} + +func newMinimizeMenuItem() *MenuItem { + return NewMenuItem("Minimize"). + SetAccelerator("CmdOrCtrl+M"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Minimise() + } + }) +} + +func newZoomMenuItem() *MenuItem { + return NewMenuItem("Zoom"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Zoom() + } + }) +} + +func newFullScreenMenuItem() *MenuItem { + return NewMenuItem("Fullscreen"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Fullscreen() + } + }) +} + +func newPrintMenuItem() *MenuItem { + panic("Implement me") +} + +func newPageLayoutMenuItem() *MenuItem { + panic("Implement me") +} + +func newShowAllMenuItem() *MenuItem { + panic("Implement me") +} + +func newBringAllToFrontMenuItem() *MenuItem { + panic("Implement me") +} + +func newNewFileMenuItem() *MenuItem { + panic("Implement me") +} + +func newOpenMenuItem() *MenuItem { + panic("Implement me") +} + +func newSaveMenuItem() *MenuItem { + panic("Implement me") +} + +func newSaveAsMenuItem() *MenuItem { + panic("Implement me") +} + +func newStartSpeakingMenuItem() *MenuItem { + panic("Implement me") +} + +func newStopSpeakingMenuItem() *MenuItem { + panic("Implement me") +} + +func newRevertMenuItem() *MenuItem { + panic("Implement me") +} + +func newFindMenuItem() *MenuItem { + panic("Implement me") +} + +func newFindAndReplaceMenuItem() *MenuItem { + panic("Implement me") +} + +func newFindNextMenuItem() *MenuItem { + panic("Implement me") +} + +func newFindPreviousMenuItem() *MenuItem { + panic("Implement me") +} + +func newHelpMenuItem() *MenuItem { + panic("Implement me") +} diff --git a/v3/pkg/application/menuitem_production.go b/v3/pkg/application/menuitem_production.go new file mode 100644 index 000000000..b5aac387f --- /dev/null +++ b/v3/pkg/application/menuitem_production.go @@ -0,0 +1,7 @@ +//go:build production && !devtools + +package application + +func NewOpenDevToolsMenuItem() *MenuItem { + return nil +} diff --git a/v3/pkg/application/menuitem_roles.go b/v3/pkg/application/menuitem_roles.go new file mode 100644 index 000000000..f36908884 --- /dev/null +++ b/v3/pkg/application/menuitem_roles.go @@ -0,0 +1,358 @@ +package application + +import "runtime" + +func NewSpeechMenu() *MenuItem { + speechMenu := NewMenu() + speechMenu.AddRole(StartSpeaking) + speechMenu.AddRole(StopSpeaking) + subMenu := NewSubMenuItem("Speech") + subMenu.submenu = speechMenu + return subMenu +} + +func NewHideMenuItem() *MenuItem { + return NewMenuItem("Hide " + globalApplication.options.Name). + SetAccelerator("CmdOrCtrl+h"). + SetRole(Hide) +} + +func NewHideOthersMenuItem() *MenuItem { + return NewMenuItem("Hide Others"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+h"). + SetRole(HideOthers) +} + +func NewFrontMenuItem() *MenuItem { + return NewMenuItem("Bring All to Front") +} + +func NewUnhideMenuItem() *MenuItem { + return NewMenuItem("Show All") +} + +func NewUndoMenuItem() *MenuItem { + result := NewMenuItem("Undo"). + SetAccelerator("CmdOrCtrl+z") + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.undo() + } + }) + } + return result +} + +// NewRedoMenuItem creates a new menu item for redoing the last action +func NewRedoMenuItem() *MenuItem { + result := NewMenuItem("Redo"). + SetAccelerator("CmdOrCtrl+Shift+z") + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.redo() + } + }) + } + return result +} + +func NewCutMenuItem() *MenuItem { + result := NewMenuItem("Cut"). + SetAccelerator("CmdOrCtrl+x") + + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.cut() + } + }) + } + return result +} + +func NewCopyMenuItem() *MenuItem { + result := NewMenuItem("Copy"). + SetAccelerator("CmdOrCtrl+c") + + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.copy() + } + }) + } + return result +} + +func NewPasteMenuItem() *MenuItem { + result := NewMenuItem("Paste"). + SetAccelerator("CmdOrCtrl+v") + + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.paste() + } + }) + } + return result +} + +func NewPasteAndMatchStyleMenuItem() *MenuItem { + return NewMenuItem("Paste and Match Style"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+v") +} + +func NewDeleteMenuItem() *MenuItem { + result := NewMenuItem("Delete"). + SetAccelerator("backspace") + + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.delete() + } + }) + } + return result +} + +func NewQuitMenuItem() *MenuItem { + label := "Quit" + if runtime.GOOS == "darwin" { + if globalApplication.options.Name != "" { + label += " " + globalApplication.options.Name + } + } + return NewMenuItem(label). + SetAccelerator("CmdOrCtrl+q"). + OnClick(func(ctx *Context) { + globalApplication.Quit() + }) +} + +func NewSelectAllMenuItem() *MenuItem { + result := NewMenuItem("Select All"). + SetAccelerator("CmdOrCtrl+a") + + if runtime.GOOS != "darwin" { + result.OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.selectAll() + } + }) + } + return result +} + +func NewAboutMenuItem() *MenuItem { + label := "About" + if globalApplication.options.Name != "" { + label += " " + globalApplication.options.Name + } + return NewMenuItem(label). + OnClick(func(ctx *Context) { + globalApplication.Menu.ShowAbout() + }) +} + +func NewCloseMenuItem() *MenuItem { + return NewMenuItem("Close"). + SetAccelerator("CmdOrCtrl+w"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Close() + } + }) +} + +func NewReloadMenuItem() *MenuItem { + return NewMenuItem("Reload"). + SetAccelerator("CmdOrCtrl+r"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Reload() + } + }) +} + +func NewForceReloadMenuItem() *MenuItem { + return NewMenuItem("Force Reload"). + SetAccelerator("CmdOrCtrl+Shift+r"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ForceReload() + } + }) +} + +func NewToggleFullscreenMenuItem() *MenuItem { + result := NewMenuItem("Toggle Full Screen"). + SetAccelerator("Ctrl+Command+F"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ToggleFullscreen() + } + }) + if runtime.GOOS != "darwin" { + result.SetAccelerator("F11") + } + return result +} + +func NewZoomResetMenuItem() *MenuItem { + // reset zoom menu item + return NewMenuItem("Actual Size"). + SetAccelerator("CmdOrCtrl+0"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ZoomReset() + } + }) +} + +func NewZoomInMenuItem() *MenuItem { + return NewMenuItem("Zoom In"). + SetAccelerator("CmdOrCtrl+plus"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ZoomIn() + } + }) +} + +func NewZoomOutMenuItem() *MenuItem { + return NewMenuItem("Zoom Out"). + SetAccelerator("CmdOrCtrl+-"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.ZoomOut() + } + }) +} + +func NewMinimiseMenuItem() *MenuItem { + return NewMenuItem("Minimize"). + SetAccelerator("CmdOrCtrl+M"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Minimise() + } + }) +} + +func NewZoomMenuItem() *MenuItem { + return NewMenuItem("Zoom"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Zoom() + } + }) +} + +func NewFullScreenMenuItem() *MenuItem { + return NewMenuItem("Fullscreen"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.Window.Current() + if currentWindow != nil { + currentWindow.Fullscreen() + } + }) +} + +func NewPrintMenuItem() *MenuItem { + return NewMenuItem("Print"). + SetAccelerator("CmdOrCtrl+p") +} + +func NewPageLayoutMenuItem() *MenuItem { + return NewMenuItem("Page Setup..."). + SetAccelerator("CmdOrCtrl+Shift+p") +} + +func NewShowAllMenuItem() *MenuItem { + return NewMenuItem("Show All") +} + +func NewBringAllToFrontMenuItem() *MenuItem { + return NewMenuItem("Bring All to Front") +} + +func NewNewFileMenuItem() *MenuItem { + return NewMenuItem("New File"). + SetAccelerator("CmdOrCtrl+n") +} + +func NewOpenMenuItem() *MenuItem { + return NewMenuItem("Open..."). + SetAccelerator("CmdOrCtrl+o"). + SetRole(Open) +} + +func NewSaveMenuItem() *MenuItem { + return NewMenuItem("Save"). + SetAccelerator("CmdOrCtrl+s") +} + +func NewSaveAsMenuItem() *MenuItem { + return NewMenuItem("Save As..."). + SetAccelerator("CmdOrCtrl+Shift+s") +} + +func NewStartSpeakingMenuItem() *MenuItem { + return NewMenuItem("Start Speaking"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+.") +} + +func NewStopSpeakingMenuItem() *MenuItem { + return NewMenuItem("Stop Speaking"). + SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+,") +} + +func NewRevertMenuItem() *MenuItem { + return NewMenuItem("Revert"). + SetAccelerator("CmdOrCtrl+r") +} + +func NewFindMenuItem() *MenuItem { + return NewMenuItem("Find..."). + SetAccelerator("CmdOrCtrl+f") +} + +func NewFindAndReplaceMenuItem() *MenuItem { + return NewMenuItem("Find and Replace..."). + SetAccelerator("CmdOrCtrl+Shift+f") +} + +func NewFindNextMenuItem() *MenuItem { + return NewMenuItem("Find Next"). + SetAccelerator("CmdOrCtrl+g") +} + +func NewFindPreviousMenuItem() *MenuItem { + return NewMenuItem("Find Previous"). + SetAccelerator("CmdOrCtrl+Shift+g") +} + +func NewHelpMenuItem() *MenuItem { + return NewMenuItem("Help"). + SetAccelerator("CmdOrCtrl+?") +} diff --git a/v3/pkg/application/menuitem_selectors_darwin.go b/v3/pkg/application/menuitem_selectors_darwin.go new file mode 100644 index 000000000..3fd001897 --- /dev/null +++ b/v3/pkg/application/menuitem_selectors_darwin.go @@ -0,0 +1,57 @@ +// File: v3/pkg/application/menuitem_selectors_darwin.go + +//go:build darwin + +package application + +import "C" + +var roleToSelector = map[Role]string{ + //AppMenu: "", // This is a special case, handled separately + About: "orderFrontStandardAboutPanel:", + //ServicesMenu: "", // This is a submenu, no direct selector + Hide: "hide:", + HideOthers: "hideOtherApplications:", + ShowAll: "unhideAllApplications:", + Quit: "terminate:", + //WindowMenu: "", // This is a submenu, no direct selector + Minimise: "performMiniaturize:", + Zoom: "performZoom:", + BringAllToFront: "arrangeInFront:", + CloseWindow: "performClose:", + //EditMenu: "", // This is a submenu, no direct selector + Undo: "undo:", + Redo: "redo:", + Cut: "cut:", + Copy: "copy:", + Paste: "paste:", + Delete: "delete:", + SelectAll: "selectAll:", + //FindMenu: "", // This is a submenu, no direct selector + Find: "performTextFinderAction:", + FindAndReplace: "performTextFinderAction:", + FindNext: "performTextFinderAction:", + FindPrevious: "performTextFinderAction:", + //ViewMenu: "", // This is a submenu, no direct selector + ToggleFullscreen: "toggleFullScreen:", + //FileMenu: "", // This is a submenu, no direct selector + NewFile: "newDocument:", + Open: "openDocument:", + Save: "saveDocument:", + SaveAs: "saveDocumentAs:", + StartSpeaking: "startSpeaking:", + StopSpeaking: "stopSpeaking:", + Revert: "revertDocumentToSaved:", + Print: "printDocument:", + PageLayout: "runPageLayout:", + //HelpMenu: "", // This is a submenu, no direct selector + Help: "showHelp:", + //No: "", // No specific selector for this role +} + +func getSelectorForRole(role Role) *C.char { + if selector, ok := roleToSelector[role]; ok && selector != "" { + return C.CString(selector) + } + return nil +} diff --git a/v3/pkg/application/menuitem_test.go b/v3/pkg/application/menuitem_test.go new file mode 100644 index 000000000..b2178876f --- /dev/null +++ b/v3/pkg/application/menuitem_test.go @@ -0,0 +1,61 @@ +package application_test + +import ( + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func TestMenuItem_GetAccelerator(t *testing.T) { + tests := []struct { + name string + menuItem *application.MenuItem + expectedAcc string + }{ + { + name: "Get existing accelerator", + menuItem: application.NewMenuItem("Item 1").SetAccelerator("ctrl+a"), + expectedAcc: "Ctrl+A", + }, + { + name: "Get non-existing accelerator", + menuItem: application.NewMenuItem("Item 2"), + expectedAcc: "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + acc := test.menuItem.GetAccelerator() + if acc != test.expectedAcc { + t.Errorf("Expected accelerator to be %v, but got %v", test.expectedAcc, acc) + } + }) + } +} + +func TestMenuItem_RemoveAccelerator(t *testing.T) { + tests := []struct { + name string + menuItem *application.MenuItem + }{ + { + name: "Remove existing accelerator", + menuItem: application.NewMenuItem("Item 1").SetAccelerator("Ctrl+A"), + }, + { + name: "Remove non-existing accelerator", + menuItem: application.NewMenuItem("Item 2"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.menuItem.RemoveAccelerator() + acc := test.menuItem.GetAccelerator() + if acc != "" { + t.Errorf("Expected accelerator to be removed, but got %v", acc) + } + }) + } +} diff --git a/v3/pkg/application/menuitem_windows.go b/v3/pkg/application/menuitem_windows.go new file mode 100644 index 000000000..941bf2388 --- /dev/null +++ b/v3/pkg/application/menuitem_windows.go @@ -0,0 +1,172 @@ +//go:build windows + +package application + +import ( + "unsafe" + + "github.com/wailsapp/wails/v3/pkg/w32" +) + +type windowsMenuItem struct { + parent *Menu + menuItem *MenuItem + + hMenu w32.HMENU + id int + label string + disabled bool + checked bool + itemType menuItemType + hidden bool + submenu w32.HMENU +} + +func (m *windowsMenuItem) setHidden(hidden bool) { + if hidden && !m.hidden { + m.hidden = true + // Remove from parent menu + w32.RemoveMenu(m.hMenu, m.id, w32.MF_BYCOMMAND) + } else if !hidden && m.hidden { + m.hidden = false + // Reinsert into parent menu at correct visible position + var pos int + for _, item := range m.parent.items { + if item == m.menuItem { + break + } + if item.hidden == false { + pos++ + } + } + w32.InsertMenuItem(m.hMenu, uint32(pos), true, m.getMenuInfo()) + } +} + +func (m *windowsMenuItem) Checked() bool { + return m.checked +} + +func (m *windowsMenuItem) IsSeparator() bool { + return m.itemType == separator +} + +func (m *windowsMenuItem) IsCheckbox() bool { + return m.itemType == checkbox +} + +func (m *windowsMenuItem) IsRadio() bool { + return m.itemType == radio +} + +func (m *windowsMenuItem) Enabled() bool { + return !m.disabled +} + +func (m *windowsMenuItem) update() { + w32.SetMenuItemInfo(m.hMenu, uint32(m.id), false, m.getMenuInfo()) +} + +func (m *windowsMenuItem) setLabel(label string) { + m.label = label + m.update() +} + +func (m *windowsMenuItem) setDisabled(disabled bool) { + m.disabled = disabled + m.update() +} + +func (m *windowsMenuItem) setChecked(checked bool) { + m.checked = checked + m.update() +} + +func (m *windowsMenuItem) destroy() { + w32.RemoveMenu(m.hMenu, m.id, w32.MF_BYCOMMAND) +} + +func (m *windowsMenuItem) setAccelerator(accelerator *accelerator) { + //// Set the keyboard shortcut of the menu item + //var modifier C.int + //var key *C.char + //if accelerator != nil { + // modifier = C.int(toMacModifier(accelerator.Modifiers)) + // key = C.CString(accelerator.Key) + //} + // + //// Convert the key to a string + //C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier) +} + +func (m *windowsMenuItem) setBitmap(bitmap []byte) { + if m.menuItem.bitmap == nil { + return + } + + // Set the icon + err := w32.SetMenuIcons(m.hMenu, m.id, bitmap, nil) + if err != nil { + globalApplication.error("unable to set bitmap on menu item: %w", err) + return + } + m.update() +} + +func newMenuItemImpl(item *MenuItem, parentMenu w32.HMENU, ID int) *windowsMenuItem { + result := &windowsMenuItem{ + menuItem: item, + hMenu: parentMenu, + id: ID, + disabled: item.disabled, + checked: item.checked, + itemType: item.itemType, + label: item.label, + hidden: item.hidden, + } + + return result +} + +func (m *windowsMenuItem) setTooltip(_ string) { + // Unsupported +} + +func (m *windowsMenuItem) getMenuInfo() *w32.MENUITEMINFO { + var mii w32.MENUITEMINFO + mii.CbSize = uint32(unsafe.Sizeof(mii)) + mii.FMask = w32.MIIM_FTYPE | w32.MIIM_ID | w32.MIIM_STATE | w32.MIIM_STRING + if m.IsSeparator() { + mii.FType = w32.MFT_SEPARATOR + } else { + mii.FType = w32.MFT_STRING + if m.IsRadio() { + mii.FType |= w32.MFT_RADIOCHECK + } + thisText := m.label + if m.menuItem.accelerator != nil { + thisText += "\t" + m.menuItem.accelerator.String() + } + mii.DwTypeData = w32.MustStringToUTF16Ptr(thisText) + mii.Cch = uint32(len([]rune(thisText))) + } + mii.WID = uint32(m.id) + if m.Enabled() { + mii.FState &^= w32.MFS_DISABLED + } else { + mii.FState |= w32.MFS_DISABLED + } + + if m.IsCheckbox() || m.IsRadio() { + mii.FMask |= w32.MIIM_CHECKMARKS + } + if m.Checked() { + mii.FState |= w32.MFS_CHECKED + } + + if m.menuItem.submenu != nil { + mii.FMask |= w32.MIIM_SUBMENU + mii.HSubMenu = m.submenu + } + return &mii +} diff --git a/v3/pkg/application/messageprocessor.go b/v3/pkg/application/messageprocessor.go new file mode 100644 index 000000000..5dd1d0f70 --- /dev/null +++ b/v3/pkg/application/messageprocessor.go @@ -0,0 +1,185 @@ +package application + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "log/slog" + "net/http" + "strconv" + "sync" + "math" +) + +// TODO maybe we could use a new struct that has the targetWindow as an attribute so we could get rid of passing the targetWindow +// as parameter through every function call. + +const ( + callRequest = 0 + clipboardRequest = 1 + applicationRequest = 2 + eventsRequest = 3 + contextMenuRequest = 4 + dialogRequest = 5 + windowRequest = 6 + screensRequest = 7 + systemRequest = 8 + browserRequest = 9 + cancelCallRequesst = 10 +) + +type MessageProcessor struct { + logger *slog.Logger + + runningCalls map[string]context.CancelFunc + l sync.Mutex +} + +func NewMessageProcessor(logger *slog.Logger) *MessageProcessor { + return &MessageProcessor{ + logger: logger, + runningCalls: map[string]context.CancelFunc{}, + } +} + +func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, err error) { + m.Error(message, "error", err) + rw.WriteHeader(http.StatusUnprocessableEntity) + _, err = rw.Write([]byte(err.Error())) + if err != nil { + m.Error("Unable to write error response:", "error", err) + } +} + +func (m *MessageProcessor) getTargetWindow(r *http.Request) (Window, string) { + windowName := r.Header.Get(webViewRequestHeaderWindowName) + if windowName != "" { + window, _ := globalApplication.Window.GetByName(windowName) + return window, windowName + } + windowID := r.Header.Get(webViewRequestHeaderWindowId) + if windowID == "" { + return nil, windowID + } + wID, err := strconv.ParseUint(windowID, 10, 64) + if err != nil { + m.Error("Window ID not parsable:", "id", windowID, "error", err) + return nil, windowID + } + // Check if wID is within the valid range for uint + if wID > math.MaxUint32 { + m.Error("Window ID out of range for uint:", "id", wID) + return nil, windowID + } + targetWindow, _ := globalApplication.Window.GetByID(uint(wID)) + if targetWindow == nil { + m.Error("Window ID not found:", "id", wID) + return nil, windowID + } + return targetWindow, windowID +} + +func (m *MessageProcessor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + object := r.URL.Query().Get("object") + if object == "" { + m.httpError(rw, "Invalid runtime call:", errors.New("missing object value")) + return + } + + m.HandleRuntimeCallWithIDs(rw, r) +} + +func (m *MessageProcessor) HandleRuntimeCallWithIDs(rw http.ResponseWriter, r *http.Request) { + defer func() { + if handlePanic() { + rw.WriteHeader(http.StatusInternalServerError) + } + }() + object, err := strconv.Atoi(r.URL.Query().Get("object")) + if err != nil { + m.httpError(rw, "Invalid runtime call:", fmt.Errorf("error decoding object value: %w", err)) + return + } + method, err := strconv.Atoi(r.URL.Query().Get("method")) + if err != nil { + m.httpError(rw, "Invalid runtime call:", fmt.Errorf("error decoding method value: %w", err)) + return + } + params := QueryParams(r.URL.Query()) + + targetWindow, nameOrID := m.getTargetWindow(r) + if targetWindow == nil { + m.httpError(rw, "Invalid runtime call:", fmt.Errorf("window '%s' not found", nameOrID)) + return + } + + switch object { + case windowRequest: + m.processWindowMethod(method, rw, r, targetWindow, params) + case clipboardRequest: + m.processClipboardMethod(method, rw, r, targetWindow, params) + case dialogRequest: + m.processDialogMethod(method, rw, r, targetWindow, params) + case eventsRequest: + m.processEventsMethod(method, rw, r, targetWindow, params) + case applicationRequest: + m.processApplicationMethod(method, rw, r, targetWindow, params) + case contextMenuRequest: + m.processContextMenuMethod(method, rw, r, targetWindow, params) + case screensRequest: + m.processScreensMethod(method, rw, r, targetWindow, params) + case callRequest: + m.processCallMethod(method, rw, r, targetWindow, params) + case systemRequest: + m.processSystemMethod(method, rw, r, targetWindow, params) + case browserRequest: + m.processBrowserMethod(method, rw, r, targetWindow, params) + case cancelCallRequesst: + m.processCallCancelMethod(method, rw, r, targetWindow, params) + default: + m.httpError(rw, "Invalid runtime call:", fmt.Errorf("unknown object %d", object)) + } +} + +func (m *MessageProcessor) Error(message string, args ...any) { + m.logger.Error(message, args...) +} + +func (m *MessageProcessor) Info(message string, args ...any) { + m.logger.Info(message, args...) +} + +func (m *MessageProcessor) json(rw http.ResponseWriter, data any) { + rw.Header().Set("Content-Type", "application/json") + // convert data to json + var jsonPayload = []byte("{}") + var err error + if data != nil { + jsonPayload, err = json.Marshal(data) + if err != nil { + m.Error("Unable to convert data to JSON. Please report this to the Wails team!", "error", err) + return + } + } + _, err = rw.Write(jsonPayload) + if err != nil { + m.Error("Unable to write json payload. Please report this to the Wails team!", "error", err) + return + } + m.ok(rw) +} + +func (m *MessageProcessor) text(rw http.ResponseWriter, data string) { + _, err := rw.Write([]byte(data)) + if err != nil { + m.Error("Unable to write json payload. Please report this to the Wails team!", "error", err) + return + } + rw.Header().Set("Content-Type", "text/plain") + rw.WriteHeader(http.StatusOK) +} + +func (m *MessageProcessor) ok(rw http.ResponseWriter) { + rw.WriteHeader(http.StatusOK) +} diff --git a/v3/pkg/application/messageprocessor_application.go b/v3/pkg/application/messageprocessor_application.go new file mode 100644 index 000000000..8297dfcc7 --- /dev/null +++ b/v3/pkg/application/messageprocessor_application.go @@ -0,0 +1,43 @@ +package application + +import ( + "fmt" + "net/http" +) + +const ( + ApplicationHide = 0 + ApplicationShow = 1 + ApplicationQuit = 2 +) + +var applicationMethodNames = map[int]string{ + ApplicationQuit: "Quit", + ApplicationHide: "Hide", + ApplicationShow: "Show", +} + +func (m *MessageProcessor) processApplicationMethod( + method int, + rw http.ResponseWriter, + r *http.Request, + window Window, + params QueryParams, +) { + switch method { + case ApplicationQuit: + globalApplication.Quit() + m.ok(rw) + case ApplicationHide: + globalApplication.Hide() + m.ok(rw) + case ApplicationShow: + globalApplication.Show() + m.ok(rw) + default: + m.httpError(rw, "Invalid application call:", fmt.Errorf("unknown method: %d", method)) + return + } + + m.Info("Runtime call:", "method", "Application."+applicationMethodNames[method]) +} diff --git a/v3/pkg/application/messageprocessor_browser.go b/v3/pkg/application/messageprocessor_browser.go new file mode 100644 index 000000000..46c428b38 --- /dev/null +++ b/v3/pkg/application/messageprocessor_browser.go @@ -0,0 +1,53 @@ +package application + +import ( + "errors" + "fmt" + "net/http" + + "github.com/pkg/browser" +) + +const ( + BrowserOpenURL = 0 +) + +var browserMethods = map[int]string{ + BrowserOpenURL: "OpenURL", +} + +func (m *MessageProcessor) processBrowserMethod(method int, rw http.ResponseWriter, _ *http.Request, _ Window, params QueryParams) { + args, err := params.Args() + if err != nil { + m.httpError(rw, "Invalid browser call:", fmt.Errorf("unable to parse arguments: %w", err)) + return + } + + switch method { + case BrowserOpenURL: + url := args.String("url") + if url == nil { + m.httpError(rw, "Invalid browser call:", errors.New("missing argument 'url'")) + return + } + + sanitizedURL, err := ValidateAndSanitizeURL(*url) + if err != nil { + m.Error("OpenURL: invalid URL - %s", err.Error()) + m.httpError(rw, fmt.Sprintf("Invalid URL: %s", err.Error()), err) + return + } + + err = browser.OpenURL(sanitizedURL) + if err != nil { + m.httpError(rw, "OpenURL failed:", err) + return + } + + m.ok(rw) + m.Info("Runtime call:", "method", "Browser."+browserMethods[method], "url", sanitizedURL) + default: + m.httpError(rw, "Invalid browser call:", fmt.Errorf("unknown method: %d", method)) + return + } +} diff --git a/v3/pkg/application/messageprocessor_call.go b/v3/pkg/application/messageprocessor_call.go new file mode 100644 index 000000000..dc25d375c --- /dev/null +++ b/v3/pkg/application/messageprocessor_call.go @@ -0,0 +1,195 @@ +package application + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +type contextKey string + +const ( + CallBinding = 0 + WindowKey contextKey = "Window" +) + +func (m *MessageProcessor) callErrorCallback(window Window, message string, callID *string, err error) { + m.Error(message, "id", *callID, "error", err) + if cerr := (*CallError)(nil); errors.As(err, &cerr) { + if data, jsonErr := json.Marshal(cerr); jsonErr == nil { + window.CallError(*callID, string(data), true) + return + } else { + m.Error("Unable to convert data to JSON. Please report this to the Wails team!", "id", *callID, "error", jsonErr) + } + } + + window.CallError(*callID, err.Error(), false) +} + +func (m *MessageProcessor) callCallback(window Window, callID *string, result string) { + window.CallResponse(*callID, result) +} + +func (m *MessageProcessor) processCallCancelMethod(method int, rw http.ResponseWriter, r *http.Request, window Window, params QueryParams) { + args, err := params.Args() + if err != nil { + m.httpError(rw, "Invalid binding call:", fmt.Errorf("unable to parse arguments: %w", err)) + return + } + + callID := args.String("call-id") + if callID == nil || *callID == "" { + m.httpError(rw, "Invalid binding call:", errors.New("missing argument 'call-id'")) + return + } + + var cancel func() + func() { + m.l.Lock() + defer m.l.Unlock() + cancel = m.runningCalls[*callID] + }() + + if cancel != nil { + cancel() + m.Info("Binding call canceled:", "id", *callID) + } + m.ok(rw) +} + +func (m *MessageProcessor) processCallMethod(method int, rw http.ResponseWriter, r *http.Request, window Window, params QueryParams) { + args, err := params.Args() + if err != nil { + m.httpError(rw, "Invalid binding call:", fmt.Errorf("unable to parse arguments: %w", err)) + return + } + + callID := args.String("call-id") + if callID == nil || *callID == "" { + m.httpError(rw, "Invalid binding call:", errors.New("missing argument 'call-id'")) + return + } + + switch method { + case CallBinding: + var options CallOptions + err := params.ToStruct(&options) + if err != nil { + m.httpError(rw, "Invalid binding call:", fmt.Errorf("error parsing call options: %w", err)) + return + } + + ctx, cancel := context.WithCancel(context.WithoutCancel(r.Context())) + + // Schedule cancel in case panics happen before starting the call. + cancelRequired := true + defer func() { + if cancelRequired { + cancel() + } + }() + + ambiguousID := false + func() { + m.l.Lock() + defer m.l.Unlock() + + if m.runningCalls[*callID] != nil { + ambiguousID = true + } else { + m.runningCalls[*callID] = cancel + } + }() + + if ambiguousID { + m.httpError(rw, "Invalid binding call:", fmt.Errorf("ambiguous call id: %s", *callID)) + return + } + + m.ok(rw) // From now on, failures are reported through the error callback. + + // Log call + var methodRef any = options.MethodName + if options.MethodName == "" { + methodRef = options.MethodID + } + m.Info("Binding call started:", "id", *callID, "method", methodRef) + + go func() { + defer handlePanic() + defer func() { + m.l.Lock() + defer m.l.Unlock() + delete(m.runningCalls, *callID) + }() + defer cancel() + + var boundMethod *BoundMethod + if options.MethodName != "" { + boundMethod = globalApplication.bindings.Get(&options) + if boundMethod == nil { + m.callErrorCallback(window, "Binding call failed:", callID, &CallError{ + Kind: ReferenceError, + Message: fmt.Sprintf("unknown bound method name '%s'", options.MethodName), + }) + return + } + } else { + boundMethod = globalApplication.bindings.GetByID(options.MethodID) + if boundMethod == nil { + m.callErrorCallback(window, "Binding call failed:", callID, &CallError{ + Kind: ReferenceError, + Message: fmt.Sprintf("unknown bound method id %d", options.MethodID), + }) + return + } + } + + // Prepare args for logging. This should never fail since json.Unmarshal succeeded before. + jsonArgs, _ := json.Marshal(options.Args) + var jsonResult []byte + defer func() { + m.Info("Binding call complete:", "id", *callID, "method", boundMethod, "args", string(jsonArgs), "result", string(jsonResult)) + }() + + // Set the context values for the window + if window != nil { + ctx = context.WithValue(ctx, WindowKey, window) + } + + result, err := boundMethod.Call(ctx, options.Args) + if cerr := (*CallError)(nil); errors.As(err, &cerr) { + switch cerr.Kind { + case ReferenceError, TypeError: + m.callErrorCallback(window, "Binding call failed:", callID, cerr) + case RuntimeError: + m.callErrorCallback(window, "Bound method returned an error:", callID, cerr) + } + return + } + + if result != nil { + // convert result to json + jsonResult, err = json.Marshal(result) + if err != nil { + m.callErrorCallback(window, "Binding call failed:", callID, &CallError{ + Kind: TypeError, + Message: fmt.Sprintf("error marshaling result: %s", err), + }) + return + } + } + + m.callCallback(window, callID, string(jsonResult)) + }() + + cancelRequired = false + + default: + m.httpError(rw, "Invalid binding call:", fmt.Errorf("unknown method: %d", method)) + return + } +} diff --git a/v3/pkg/application/messageprocessor_clipboard.go b/v3/pkg/application/messageprocessor_clipboard.go new file mode 100644 index 000000000..d77b6459d --- /dev/null +++ b/v3/pkg/application/messageprocessor_clipboard.go @@ -0,0 +1,47 @@ +package application + +import ( + "errors" + "fmt" + "net/http" +) + +const ( + ClipboardSetText = 0 + ClipboardText = 1 +) + +var clipboardMethods = map[int]string{ + ClipboardSetText: "SetText", + ClipboardText: "Text", +} + +func (m *MessageProcessor) processClipboardMethod(method int, rw http.ResponseWriter, _ *http.Request, _ Window, params QueryParams) { + args, err := params.Args() + if err != nil { + m.httpError(rw, "Invalid clipboard call:", fmt.Errorf("unable to parse arguments: %w", err)) + return + } + + var text string + + switch method { + case ClipboardSetText: + textp := args.String("text") + if textp == nil { + m.httpError(rw, "Invalid clipboard call:", errors.New("missing argument 'text'")) + return + } + text = *textp + globalApplication.Clipboard.SetText(text) + m.ok(rw) + case ClipboardText: + text, _ = globalApplication.Clipboard.Text() + m.text(rw, text) + default: + m.httpError(rw, "Invalid clipboard call:", fmt.Errorf("unknown method: %d", method)) + return + } + + m.Info("Runtime call:", "method", "Clipboard."+clipboardMethods[method], "text", text) +} diff --git a/v3/pkg/application/messageprocessor_contextmenu.go b/v3/pkg/application/messageprocessor_contextmenu.go new file mode 100644 index 000000000..c315db15a --- /dev/null +++ b/v3/pkg/application/messageprocessor_contextmenu.go @@ -0,0 +1,50 @@ +package application + +import ( + "fmt" + "net/http" +) + +type ContextMenuData struct { + Id string `json:"id"` + X int `json:"x"` + Y int `json:"y"` + Data string `json:"data"` +} + +func (d ContextMenuData) clone() *ContextMenuData { + return &ContextMenuData{ + Id: d.Id, + X: d.X, + Y: d.Y, + Data: d.Data, + } +} + +const ( + ContextMenuOpen = 0 +) + +var contextmenuMethodNames = map[int]string{ + ContextMenuOpen: "Open", +} + +func (m *MessageProcessor) processContextMenuMethod(method int, rw http.ResponseWriter, _ *http.Request, window Window, params QueryParams) { + switch method { + case ContextMenuOpen: + var data ContextMenuData + err := params.ToStruct(&data) + if err != nil { + m.httpError(rw, "Invalid contextmenu call:", fmt.Errorf("error parsing parameters: %w", err)) + return + } + + window.OpenContextMenu(&data) + + m.ok(rw) + m.Info("Runtime call:", "method", "ContextMenu."+contextmenuMethodNames[method], "id", data.Id, "x", data.X, "y", data.Y, "data", data.Data) + default: + m.httpError(rw, "Invalid contextmenu call:", fmt.Errorf("unknown method: %d", method)) + return + } +} diff --git a/v3/pkg/application/messageprocessor_dialog.go b/v3/pkg/application/messageprocessor_dialog.go new file mode 100644 index 000000000..e53ba0c4c --- /dev/null +++ b/v3/pkg/application/messageprocessor_dialog.go @@ -0,0 +1,168 @@ +package application + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "runtime" +) + +const ( + DialogInfo = 0 + DialogWarning = 1 + DialogError = 2 + DialogQuestion = 3 + DialogOpenFile = 4 + DialogSaveFile = 5 +) + +var dialogMethodNames = map[int]string{ + DialogInfo: "Info", + DialogWarning: "Warning", + DialogError: "Error", + DialogQuestion: "Question", + DialogOpenFile: "OpenFile", + DialogSaveFile: "SaveFile", +} + +func (m *MessageProcessor) dialogErrorCallback(window Window, message string, dialogID *string, err error) { + m.Error(message, "error", err) + window.DialogError(*dialogID, err.Error()) +} + +func (m *MessageProcessor) dialogCallback(window Window, dialogID *string, result string, isJSON bool) { + window.DialogResponse(*dialogID, result, isJSON) +} + +func (m *MessageProcessor) processDialogMethod(method int, rw http.ResponseWriter, r *http.Request, window Window, params QueryParams) { + args, err := params.Args() + if err != nil { + m.httpError(rw, "Invalid dialog call:", fmt.Errorf("unable to parse arguments: %w", err)) + return + } + + dialogID := args.String("dialog-id") + if dialogID == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing argument 'dialog-id'")) + return + } + + var methodName = "Dialog." + dialogMethodNames[method] + + switch method { + case DialogInfo, DialogWarning, DialogError, DialogQuestion: + var options MessageDialogOptions + err := params.ToStruct(&options) + if err != nil { + m.httpError(rw, "Invalid dialog call:", fmt.Errorf("error parsing dialog options: %w", err)) + return + } + if len(options.Buttons) == 0 { + switch runtime.GOOS { + case "darwin": + options.Buttons = []*Button{{Label: "OK", IsDefault: true}} + } + } + var dialog *MessageDialog + switch method { + case DialogInfo: + dialog = InfoDialog() + case DialogWarning: + dialog = WarningDialog() + case DialogError: + dialog = ErrorDialog() + case DialogQuestion: + dialog = QuestionDialog() + } + var detached = args.Bool("Detached") + if detached == nil || !*detached { + dialog.AttachToWindow(window) + } + + dialog.SetTitle(options.Title) + dialog.SetMessage(options.Message) + for _, button := range options.Buttons { + label := button.Label + button.OnClick(func() { + m.dialogCallback(window, dialogID, label, false) + }) + } + dialog.AddButtons(options.Buttons) + dialog.Show() + m.ok(rw) + m.Info("Runtime call:", "method", methodName, "options", options) + + case DialogOpenFile: + var options OpenFileDialogOptions + err := params.ToStruct(&options) + if err != nil { + m.httpError(rw, "Invalid dialog call:", fmt.Errorf("error parsing dialog options: %w", err)) + return + } + var detached = args.Bool("Detached") + if detached == nil || !*detached { + options.Window = window + } + dialog := globalApplication.Dialog.OpenFileWithOptions(&options) + + go func() { + defer handlePanic() + if options.AllowsMultipleSelection { + files, err := dialog.PromptForMultipleSelection() + if err != nil { + m.dialogErrorCallback(window, "Dialog.OpenFile failed", dialogID, fmt.Errorf("error getting selection: %w", err)) + return + } else { + result, err := json.Marshal(files) + if err != nil { + m.dialogErrorCallback(window, "Dialog.OpenFile failed", dialogID, fmt.Errorf("error marshaling files: %w", err)) + return + } + m.dialogCallback(window, dialogID, string(result), true) + m.Info("Runtime call:", "method", methodName, "result", result) + } + } else { + file, err := dialog.PromptForSingleSelection() + if err != nil { + m.dialogErrorCallback(window, "Dialog.OpenFile failed", dialogID, fmt.Errorf("error getting selection: %w", err)) + return + } + m.dialogCallback(window, dialogID, file, false) + m.Info("Runtime call:", "method", methodName, "result", file) + } + }() + m.ok(rw) + m.Info("Runtime call:", "method", methodName, "options", options) + + case DialogSaveFile: + var options SaveFileDialogOptions + err := params.ToStruct(&options) + if err != nil { + m.httpError(rw, "Invalid dialog call:", fmt.Errorf("error parsing dialog options: %w", err)) + return + } + var detached = args.Bool("Detached") + if detached == nil || !*detached { + options.Window = window + } + dialog := globalApplication.Dialog.SaveFileWithOptions(&options) + + go func() { + defer handlePanic() + file, err := dialog.PromptForSingleSelection() + if err != nil { + m.dialogErrorCallback(window, "Dialog.SaveFile failed", dialogID, fmt.Errorf("error getting selection: %w", err)) + return + } + m.dialogCallback(window, dialogID, file, false) + m.Info("Runtime call:", "method", methodName, "result", file) + }() + m.ok(rw) + m.Info("Runtime call:", "method", methodName, "options", options) + + default: + m.httpError(rw, "Invalid dialog call:", fmt.Errorf("unknown method: %d", method)) + return + } +} diff --git a/v3/pkg/application/messageprocessor_events.go b/v3/pkg/application/messageprocessor_events.go new file mode 100644 index 000000000..93ab34064 --- /dev/null +++ b/v3/pkg/application/messageprocessor_events.go @@ -0,0 +1,41 @@ +package application + +import ( + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +const ( + EventsEmit = 0 +) + +var eventsMethodNames = map[int]string{ + EventsEmit: "Emit", +} + +func (m *MessageProcessor) processEventsMethod(method int, rw http.ResponseWriter, _ *http.Request, window Window, params QueryParams) { + switch method { + case EventsEmit: + var event CustomEvent + err := params.ToStruct(&event) + if err != nil { + m.httpError(rw, "Invalid events call:", fmt.Errorf("error parsing event: %w", err)) + return + } + if event.Name == "" { + m.httpError(rw, "Invalid events call:", errors.New("missing event name")) + return + } + + event.Sender = window.Name() + globalApplication.Event.EmitEvent(&event) + + m.ok(rw) + m.Info("Runtime call:", "method", "Events."+eventsMethodNames[method], "name", event.Name, "sender", event.Sender, "data", event.Data, "cancelled", event.IsCancelled()) + default: + m.httpError(rw, "Invalid events call:", fmt.Errorf("unknown method: %d", method)) + return + } +} diff --git a/v3/pkg/application/messageprocessor_params.go b/v3/pkg/application/messageprocessor_params.go new file mode 100644 index 000000000..b4f313a3a --- /dev/null +++ b/v3/pkg/application/messageprocessor_params.go @@ -0,0 +1,206 @@ +package application + +import ( + "encoding/json" + "fmt" + "strconv" +) + +type QueryParams map[string][]string + +func (qp QueryParams) String(key string) *string { + if qp == nil { + return nil + } + values := qp[key] + if len(values) == 0 { + return nil + } + return &values[0] +} + +func (qp QueryParams) Int(key string) *int { + val := qp.String(key) + if val == nil { + return nil + } + result, err := strconv.Atoi(*val) + if err != nil { + return nil + } + return &result +} + +func (qp QueryParams) UInt8(key string) *uint8 { + val := qp.String(key) + if val == nil { + return nil + } + intResult, err := strconv.Atoi(*val) + if err != nil { + return nil + } + + if intResult < 0 { + intResult = 0 + } + if intResult > 255 { + intResult = 255 + } + + var result = uint8(intResult) + + return &result +} +func (qp QueryParams) UInt(key string) *uint { + val := qp.String(key) + if val == nil { + return nil + } + intResult, err := strconv.Atoi(*val) + if err != nil { + return nil + } + + if intResult < 0 { + intResult = 0 + } + if intResult > 255 { + intResult = 255 + } + + var result = uint(intResult) + + return &result +} + +func (qp QueryParams) Bool(key string) *bool { + val := qp.String(key) + if val == nil { + return nil + } + result, err := strconv.ParseBool(*val) + if err != nil { + return nil + } + return &result +} + +func (qp QueryParams) Float64(key string) *float64 { + val := qp.String(key) + if val == nil { + return nil + } + result, err := strconv.ParseFloat(*val, 64) + if err != nil { + return nil + } + return &result +} + +func (qp QueryParams) ToStruct(str any) error { + args := qp["args"] + if len(args) == 1 { + return json.Unmarshal([]byte(args[0]), &str) + } + return nil +} + +type Args struct { + data map[string]any +} + +func (a *Args) String(key string) *string { + if a == nil { + return nil + } + if val := a.data[key]; val != nil { + result := fmt.Sprintf("%v", val) + return &result + } + return nil +} + +func (a *Args) Int(s string) *int { + if a == nil { + return nil + } + if val := a.data[s]; val != nil { + return convertNumber[int](val) + } + return nil +} + +func convertNumber[T int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64](val any) *T { + if val == nil { + return nil + } + var result T + switch v := val.(type) { + case T: + result = v + case float64: + result = T(v) + default: + return nil + } + return &result +} + +func (a *Args) UInt8(s string) *uint8 { + if a == nil { + return nil + } + if val := a.data[s]; val != nil { + return convertNumber[uint8](val) + } + return nil +} + +func (a *Args) UInt(s string) *uint { + if a == nil { + return nil + } + if val := a.data[s]; val != nil { + return convertNumber[uint](val) + } + return nil +} + +func (a *Args) Float64(s string) *float64 { + if a == nil { + return nil + } + if val := a.data[s]; val != nil { + if result, ok := val.(float64); ok { + return &result + } + } + return nil +} + +func (a *Args) Bool(s string) *bool { + if a == nil { + return nil + } + if val := a.data[s]; val != nil { + if result, ok := val.(bool); ok { + return &result + } + } + return nil +} + +func (qp QueryParams) Args() (*Args, error) { + argData := qp["args"] + var result = &Args{ + data: make(map[string]any), + } + if len(argData) == 1 { + err := json.Unmarshal([]byte(argData[0]), &result.data) + if err != nil { + return nil, err + } + } + return result, nil +} diff --git a/v3/pkg/application/messageprocessor_screens.go b/v3/pkg/application/messageprocessor_screens.go new file mode 100644 index 000000000..d90487d59 --- /dev/null +++ b/v3/pkg/application/messageprocessor_screens.go @@ -0,0 +1,42 @@ +package application + +import ( + "fmt" + "net/http" +) + +const ( + ScreensGetAll = 0 + ScreensGetPrimary = 1 + ScreensGetCurrent = 2 +) + +var screensMethodNames = map[int]string{ + ScreensGetAll: "GetAll", + ScreensGetPrimary: "GetPrimary", + ScreensGetCurrent: "GetCurrent", +} + +func (m *MessageProcessor) processScreensMethod(method int, rw http.ResponseWriter, _ *http.Request, _ Window, _ QueryParams) { + switch method { + case ScreensGetAll: + screens := globalApplication.Screen.GetAll() + m.json(rw, screens) + case ScreensGetPrimary: + screen := globalApplication.Screen.GetPrimary() + m.json(rw, screen) + case ScreensGetCurrent: + screen, err := globalApplication.Window.Current().GetScreen() + if err != nil { + m.httpError(rw, "Window.GetScreen failed:", err) + return + } + m.json(rw, screen) + default: + m.httpError(rw, "Invalid screens call:", fmt.Errorf("unknown method: %d", method)) + return + } + + m.Info("Runtime call:", "method", "Screens."+screensMethodNames[method]) + +} diff --git a/v3/pkg/application/messageprocessor_system.go b/v3/pkg/application/messageprocessor_system.go new file mode 100644 index 000000000..e1a1e771c --- /dev/null +++ b/v3/pkg/application/messageprocessor_system.go @@ -0,0 +1,30 @@ +package application + +import ( + "fmt" + "net/http" +) + +const ( + SystemIsDarkMode = 0 + Environment = 1 +) + +var systemMethodNames = map[int]string{ + SystemIsDarkMode: "IsDarkMode", + Environment: "Environment", +} + +func (m *MessageProcessor) processSystemMethod(method int, rw http.ResponseWriter, r *http.Request, window Window, params QueryParams) { + switch method { + case SystemIsDarkMode: + m.json(rw, globalApplication.Env.IsDarkMode()) + case Environment: + m.json(rw, globalApplication.Env.Info()) + default: + m.httpError(rw, "Invalid system call:", fmt.Errorf("unknown method: %d", method)) + return + } + + m.Info("Runtime call:", "method", "System."+systemMethodNames[method]) +} diff --git a/v3/pkg/application/messageprocessor_window.go b/v3/pkg/application/messageprocessor_window.go new file mode 100644 index 000000000..7c4307136 --- /dev/null +++ b/v3/pkg/application/messageprocessor_window.go @@ -0,0 +1,510 @@ +package application + +import ( + "encoding/json" + "errors" + "fmt" + "log/slog" + "net/http" +) + +const ( + WindowPosition = 0 + WindowCenter = 1 + WindowClose = 2 + WindowDisableSizeConstraints = 3 + WindowEnableSizeConstraints = 4 + WindowFocus = 5 + WindowForceReload = 6 + WindowFullscreen = 7 + WindowGetScreen = 8 + WindowGetZoom = 9 + WindowHeight = 10 + WindowHide = 11 + WindowIsFocused = 12 + WindowIsFullscreen = 13 + WindowIsMaximised = 14 + WindowIsMinimised = 15 + WindowMaximise = 16 + WindowMinimise = 17 + WindowName = 18 + WindowOpenDevTools = 19 + WindowRelativePosition = 20 + WindowReload = 21 + WindowResizable = 22 + WindowRestore = 23 + WindowSetPosition = 24 + WindowSetAlwaysOnTop = 25 + WindowSetBackgroundColour = 26 + WindowSetFrameless = 27 + WindowSetFullscreenButtonEnabled = 28 + WindowSetMaxSize = 29 + WindowSetMinSize = 30 + WindowSetRelativePosition = 31 + WindowSetResizable = 32 + WindowSetSize = 33 + WindowSetTitle = 34 + WindowSetZoom = 35 + WindowShow = 36 + WindowSize = 37 + WindowToggleFullscreen = 38 + WindowToggleMaximise = 39 + WindowToggleFrameless = 40 + WindowUnFullscreen = 41 + WindowUnMaximise = 42 + WindowUnMinimise = 43 + WindowWidth = 44 + WindowZoom = 45 + WindowZoomIn = 46 + WindowZoomOut = 47 + WindowZoomReset = 48 + WindowSnapAssist = 49 + WindowDropZoneDropped = 50 +) + +var windowMethodNames = map[int]string{ + WindowPosition: "Position", + WindowCenter: "Center", + WindowClose: "Close", + WindowDisableSizeConstraints: "DisableSizeConstraints", + WindowEnableSizeConstraints: "EnableSizeConstraints", + WindowFocus: "Focus", + WindowForceReload: "ForceReload", + WindowFullscreen: "Fullscreen", + WindowGetScreen: "GetScreen", + WindowGetZoom: "GetZoom", + WindowHeight: "Height", + WindowHide: "Hide", + WindowIsFocused: "IsFocused", + WindowIsFullscreen: "IsFullscreen", + WindowIsMaximised: "IsMaximised", + WindowIsMinimised: "IsMinimised", + WindowMaximise: "Maximise", + WindowMinimise: "Minimise", + WindowName: "Name", + WindowOpenDevTools: "OpenDevTools", + WindowRelativePosition: "RelativePosition", + WindowReload: "Reload", + WindowResizable: "Resizable", + WindowRestore: "Restore", + WindowSetPosition: "SetPosition", + WindowSetAlwaysOnTop: "SetAlwaysOnTop", + WindowSetBackgroundColour: "SetBackgroundColour", + WindowSetFrameless: "SetFrameless", + WindowSetFullscreenButtonEnabled: "SetFullscreenButtonEnabled", + WindowSetMaxSize: "SetMaxSize", + WindowSetMinSize: "SetMinSize", + WindowSetRelativePosition: "SetRelativePosition", + WindowSetResizable: "SetResizable", + WindowSetSize: "SetSize", + WindowSetTitle: "SetTitle", + WindowSetZoom: "SetZoom", + WindowShow: "Show", + WindowSize: "Size", + WindowToggleFullscreen: "ToggleFullscreen", + WindowToggleMaximise: "ToggleMaximise", + WindowToggleFrameless: "ToggleFrameless", + WindowUnFullscreen: "UnFullscreen", + WindowUnMaximise: "UnMaximise", + WindowUnMinimise: "UnMinimise", + WindowWidth: "Width", + WindowZoom: "Zoom", + WindowZoomIn: "ZoomIn", + WindowZoomOut: "ZoomOut", + WindowZoomReset: "ZoomReset", + WindowDropZoneDropped: "DropZoneDropped", + WindowSnapAssist: "SnapAssist", +} + +func (m *MessageProcessor) processWindowMethod( + method int, + rw http.ResponseWriter, + req *http.Request, + window Window, + params QueryParams, +) { + args, err := params.Args() + if err != nil { + m.httpError(rw, "Invalid window call:", fmt.Errorf("unable to parse arguments: %w", err)) + return + } + + switch method { + case WindowPosition: + x, y := window.Position() + m.json(rw, map[string]interface{}{ + "x": x, + "y": y, + }) + case WindowCenter: + window.Center() + m.ok(rw) + case WindowClose: + window.Close() + m.ok(rw) + case WindowDisableSizeConstraints: + window.DisableSizeConstraints() + m.ok(rw) + case WindowEnableSizeConstraints: + window.EnableSizeConstraints() + m.ok(rw) + case WindowFocus: + window.Focus() + m.ok(rw) + case WindowForceReload: + window.ForceReload() + m.ok(rw) + case WindowFullscreen: + window.Fullscreen() + m.ok(rw) + case WindowGetScreen: + screen, err := window.GetScreen() + if err != nil { + m.httpError(rw, "Window.GetScreen failed:", err) + return + } + m.json(rw, screen) + case WindowGetZoom: + zoom := window.GetZoom() + m.json(rw, zoom) + case WindowHeight: + height := window.Height() + m.json(rw, height) + case WindowHide: + window.Hide() + m.ok(rw) + case WindowIsFocused: + isFocused := window.IsFocused() + m.json(rw, isFocused) + case WindowIsFullscreen: + isFullscreen := window.IsFullscreen() + m.json(rw, isFullscreen) + case WindowIsMaximised: + isMaximised := window.IsMaximised() + m.json(rw, isMaximised) + case WindowIsMinimised: + isMinimised := window.IsMinimised() + m.json(rw, isMinimised) + case WindowMaximise: + window.Maximise() + m.ok(rw) + case WindowMinimise: + window.Minimise() + m.ok(rw) + case WindowName: + name := window.Name() + m.json(rw, name) + case WindowRelativePosition: + x, y := window.RelativePosition() + m.json(rw, map[string]interface{}{ + "x": x, + "y": y, + }) + case WindowReload: + window.Reload() + m.ok(rw) + case WindowResizable: + resizable := window.Resizable() + m.json(rw, resizable) + case WindowRestore: + window.Restore() + m.ok(rw) + case WindowSetPosition: + x := args.Int("x") + if x == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'x'")) + return + } + y := args.Int("y") + if y == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'y'")) + return + } + window.SetPosition(*x, *y) + m.ok(rw) + case WindowSetAlwaysOnTop: + alwaysOnTop := args.Bool("alwaysOnTop") + if alwaysOnTop == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'alwaysOnTop'"), + ) + return + } + window.SetAlwaysOnTop(*alwaysOnTop) + m.ok(rw) + case WindowSetBackgroundColour: + r := args.UInt8("r") + if r == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'r'")) + return + } + g := args.UInt8("g") + if g == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'g'")) + return + } + b := args.UInt8("b") + if b == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'b'")) + return + } + a := args.UInt8("a") + if a == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'a'")) + return + } + window.SetBackgroundColour(RGBA{ + Red: *r, + Green: *g, + Blue: *b, + Alpha: *a, + }) + m.ok(rw) + case WindowSetFrameless: + frameless := args.Bool("frameless") + if frameless == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'frameless'"), + ) + return + } + window.SetFrameless(*frameless) + m.ok(rw) + case WindowSetMaxSize: + width := args.Int("width") + if width == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'width'"), + ) + return + } + height := args.Int("height") + if height == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'height'"), + ) + return + } + window.SetMaxSize(*width, *height) + m.ok(rw) + case WindowSetMinSize: + width := args.Int("width") + if width == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'width'"), + ) + return + } + height := args.Int("height") + if height == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'height'"), + ) + return + } + window.SetMinSize(*width, *height) + m.ok(rw) + case WindowSetRelativePosition: + x := args.Int("x") + if x == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'x'")) + return + } + y := args.Int("y") + if y == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing or invalid argument 'y'")) + return + } + window.SetRelativePosition(*x, *y) + m.ok(rw) + case WindowSetResizable: + resizable := args.Bool("resizable") + if resizable == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'resizable'"), + ) + return + } + window.SetResizable(*resizable) + m.ok(rw) + case WindowSetSize: + width := args.Int("width") + if width == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'width'"), + ) + return + } + height := args.Int("height") + if height == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'height'"), + ) + return + } + window.SetSize(*width, *height) + m.ok(rw) + case WindowSetTitle: + title := args.String("title") + if title == nil { + m.httpError(rw, "Invalid window call:", errors.New("missing argument 'title'")) + return + } + window.SetTitle(*title) + m.ok(rw) + case WindowSetZoom: + zoom := args.Float64("zoom") + if zoom == nil { + m.httpError( + rw, + "Invalid window call:", + errors.New("missing or invalid argument 'zoom'"), + ) + return + } + window.SetZoom(*zoom) + m.ok(rw) + case WindowShow: + window.Show() + m.ok(rw) + case WindowSize: + width, height := window.Size() + m.json(rw, map[string]interface{}{ + "width": width, + "height": height, + }) + case WindowOpenDevTools: + window.OpenDevTools() + m.ok(rw) + case WindowToggleFullscreen: + window.ToggleFullscreen() + m.ok(rw) + case WindowToggleMaximise: + window.ToggleMaximise() + m.ok(rw) + case WindowToggleFrameless: + window.ToggleFrameless() + m.ok(rw) + case WindowUnFullscreen: + window.UnFullscreen() + m.ok(rw) + case WindowUnMaximise: + window.UnMaximise() + m.ok(rw) + case WindowUnMinimise: + window.UnMinimise() + m.ok(rw) + case WindowWidth: + width := window.Width() + m.json(rw, width) + case WindowZoom: + window.Zoom() + m.ok(rw) + case WindowZoomIn: + window.ZoomIn() + m.ok(rw) + case WindowZoomOut: + window.ZoomOut() + m.ok(rw) + case WindowZoomReset: + window.ZoomReset() + m.ok(rw) + case WindowDropZoneDropped: + m.Info( + "[DragDropDebug] processWindowMethod: Entered WindowDropZoneDropped case", + ) + + jsonArgs := params.String("args") // 'params' is the QueryParams from processWindowMethod + if jsonArgs == nil { + m.httpError(rw, "Error processing WindowDropZoneDropped: missing 'args' parameter", nil) + return + } + + slog.Info("[DragDropDebug] Raw 'args' payload string:", "data", *jsonArgs) + + var payload fileDropPayload + err := json.Unmarshal([]byte(*jsonArgs), &payload) + if err != nil { + m.httpError(rw, "Error decoding file drop payload from 'args' parameter:", err) + return + } + m.Info( + "[DragDropDebug] processWindowMethod: Decoded payload from 'args'", + "payload", + fmt.Sprintf("%+v", payload), + ) + + dropDetails := &DropZoneDetails{ + X: payload.X, + Y: payload.Y, + ElementID: payload.ElementDetails.ID, + ClassList: payload.ElementDetails.ClassList, + Attributes: payload.ElementDetails.Attributes, // Assumes DropZoneDetails struct is updated to include this field + } + + wvWindow, ok := window.(*WebviewWindow) + if !ok { + m.httpError( + rw, + "Error: Target window is not a WebviewWindow for FilesDroppedWithContext", + nil, + ) + return + } + + msg := &dragAndDropMessage{ + windowId: wvWindow.id, + filenames: payload.Filenames, + DropZone: dropDetails, + } + + m.Info( + "[DragDropDebug] processApplicationMethod: Sending message to windowDragAndDropBuffer", + "message", + fmt.Sprintf("%+v", msg), + ) + windowDragAndDropBuffer <- msg + m.ok(rw) + case WindowSnapAssist: + window.SnapAssist() + m.ok(rw) + default: + m.httpError(rw, "Invalid window call:", fmt.Errorf("unknown method %d", method)) + return + } + + m.Info("Runtime call:", "method", "Window."+windowMethodNames[method]) +} + +// ElementDetailsPayload holds detailed information about the drop target element. +type ElementDetailsPayload struct { + ID string `json:"id"` + ClassList []string `json:"classList"` + Attributes map[string]string `json:"attributes"` +} + +// Define a struct for the JSON payload from HandlePlatformFileDrop +type fileDropPayload struct { + Filenames []string `json:"filenames"` + X int `json:"x"` + Y int `json:"y"` + ElementDetails ElementDetailsPayload `json:"elementDetails"` +} diff --git a/v3/pkg/application/panic_handler.go b/v3/pkg/application/panic_handler.go new file mode 100644 index 000000000..53f42a309 --- /dev/null +++ b/v3/pkg/application/panic_handler.go @@ -0,0 +1,104 @@ +package application + +import ( + "fmt" + "runtime" + "runtime/debug" + "strings" + "time" +) + +func getStackTrace(skipStart int, skipEnd int) string { + // Get all program counters first + pc := make([]uintptr, 32) + n := runtime.Callers(skipStart+1, pc) + if n == 0 { + return "" + } + + pc = pc[:n] + frames := runtime.CallersFrames(pc) + + // Collect all frames first + var allFrames []runtime.Frame + for { + frame, more := frames.Next() + allFrames = append(allFrames, frame) + if !more { + break + } + } + + // Remove frames from the end + if len(allFrames) > skipEnd { + allFrames = allFrames[:len(allFrames)-skipEnd] + } + + // Build the output string + var builder strings.Builder + for _, frame := range allFrames { + fmt.Fprintf(&builder, "%s\n\tat %s:%d\n", + frame.Function, frame.File, frame.Line) + } + return builder.String() +} + +type handlePanicOptions struct { + skipEnd int +} + +type PanicDetails struct { + StackTrace string + Error error + Time time.Time + FullStackTrace string +} + +func newPanicDetails(err error, trace string) *PanicDetails { + return &PanicDetails{ + Error: err, + Time: time.Now(), + StackTrace: trace, + FullStackTrace: string(debug.Stack()), + } +} + +// handlePanic handles any panics +// Returns the error if there was one +func handlePanic(options ...handlePanicOptions) bool { + // Try to recover + e := recover() + if e == nil { + return false + } + + // Get the error + err, ok := e.(error) + if !ok { + err = fmt.Errorf("%v", e) + } + + // Get the stack trace + var stackTrace string + skipEnd := 0 + if len(options) > 0 { + skipEnd = options[0].skipEnd + } + stackTrace = getStackTrace(3, skipEnd) + + processPanic(newPanicDetails(err, stackTrace)) + return false +} + +func processPanic(panicDetails *PanicDetails) { + h := globalApplication.options.PanicHandler + if h != nil { + h(panicDetails) + return + } + defaultPanicHandler(panicDetails) +} + +func defaultPanicHandler(panicDetails *PanicDetails) { + globalApplication.fatal("panic error: %w\n%s", panicDetails.Error, panicDetails.StackTrace) +} diff --git a/v3/pkg/application/path.go b/v3/pkg/application/path.go new file mode 100644 index 000000000..44066fa96 --- /dev/null +++ b/v3/pkg/application/path.go @@ -0,0 +1,133 @@ +package application + +import "github.com/adrg/xdg" + +type PathType int + +const ( + // PathHome is the user's home directory. + PathHome PathType = iota + + // PathDataHome defines the base directory relative to which user-specific + // data files should be stored. This directory is defined by the + // $XDG_DATA_HOME environment variable. If the variable is not set, + // a default equal to $HOME/.local/share should be used. + PathDataHome + + // PathConfigHome defines the base directory relative to which user-specific + // configuration files should be written. This directory is defined by + // the $XDG_CONFIG_HOME environment variable. If the variable is + // not set, a default equal to $HOME/.config should be used. + PathConfigHome + + // PathStateHome defines the base directory relative to which user-specific + // state files should be stored. This directory is defined by the + // $XDG_STATE_HOME environment variable. If the variable is not set, + // a default equal to ~/.local/state should be used. + PathStateHome + + // PathCacheHome defines the base directory relative to which user-specific + // non-essential (cached) data should be written. This directory is + // defined by the $XDG_CACHE_HOME environment variable. If the variable + // is not set, a default equal to $HOME/.cache should be used. + PathCacheHome + + // PathRuntimeDir defines the base directory relative to which user-specific + // non-essential runtime files and other file objects (such as sockets, + // named pipes, etc.) should be stored. This directory is defined by the + // $XDG_RUNTIME_DIR environment variable. If the variable is not set, + // applications should fall back to a replacement directory with similar + // capabilities. Applications should use this directory for communication + // and synchronization purposes and should not place larger files in it, + // since it might reside in runtime memory and cannot necessarily be + // swapped out to disk. + PathRuntimeDir + + // PathDesktop defines the location of the user's desktop directory. + PathDesktop + + // PathDownload defines a suitable location for user downloaded files. + PathDownload + + // PathDocuments defines a suitable location for user document files. + PathDocuments + + // PathMusic defines a suitable location for user audio files. + PathMusic + + // PathPictures defines a suitable location for user image files. + PathPictures + + // PathVideos defines a suitable location for user video files. + PathVideos + + // PathTemplates defines a suitable location for user template files. + PathTemplates + + // PathPublicShare defines a suitable location for user shared files. + PathPublicShare +) + +var paths = map[PathType]string{ + PathHome: xdg.Home, + PathDataHome: xdg.DataHome, + PathConfigHome: xdg.ConfigHome, + PathStateHome: xdg.StateHome, + PathCacheHome: xdg.CacheHome, + PathRuntimeDir: xdg.RuntimeDir, + PathDesktop: xdg.UserDirs.Desktop, + PathDownload: xdg.UserDirs.Download, + PathDocuments: xdg.UserDirs.Documents, + PathMusic: xdg.UserDirs.Music, + PathPictures: xdg.UserDirs.Pictures, + PathVideos: xdg.UserDirs.Videos, + PathTemplates: xdg.UserDirs.Templates, + PathPublicShare: xdg.UserDirs.PublicShare, +} + +type PathTypes int + +const ( + // PathsDataDirs defines the preference-ordered set of base directories to + // search for data files in addition to the DataHome base directory. + // This set of directories is defined by the $XDG_DATA_DIRS environment + // variable. If the variable is not set, the default directories + // to be used are /usr/local/share and /usr/share, in that order. The + // DataHome directory is considered more important than any of the + // directories defined by DataDirs. Therefore, user data files should be + // written relative to the DataHome directory, if possible. + PathsDataDirs PathTypes = iota + + // PathsConfigDirs defines the preference-ordered set of base directories + // search for configuration files in addition to the ConfigHome base + // directory. This set of directories is defined by the $XDG_CONFIG_DIRS + // environment variable. If the variable is not set, a default equal + // to /etc/xdg should be used. The ConfigHome directory is considered + // more important than any of the directories defined by ConfigDirs. + // Therefore, user config files should be written relative to the + // ConfigHome directory, if possible. + PathsConfigDirs + + // PathsFontDirs defines the common locations where font files are stored. + PathsFontDirs + + // PathsApplicationDirs defines the common locations of applications. + PathsApplicationDirs +) + +var pathdirs = map[PathTypes][]string{ + PathsDataDirs: xdg.DataDirs, + PathsConfigDirs: xdg.ConfigDirs, + PathsFontDirs: xdg.FontDirs, + PathsApplicationDirs: xdg.ApplicationDirs, +} + +// Path returns the path for the given selector +func Path(selector PathType) string { + return paths[selector] +} + +// Paths returns the paths for the given selector +func Paths(selector PathTypes) []string { + return pathdirs[selector] +} diff --git a/v3/pkg/application/popupmenu_windows.go b/v3/pkg/application/popupmenu_windows.go new file mode 100644 index 000000000..f237b2669 --- /dev/null +++ b/v3/pkg/application/popupmenu_windows.go @@ -0,0 +1,302 @@ +package application + +import ( + "github.com/wailsapp/wails/v3/pkg/w32" + "unsafe" +) + +const ( + MenuItemMsgID = w32.WM_APP + 1024 +) + +type RadioGroupMember struct { + ID int + MenuItem *MenuItem +} + +type RadioGroup []*RadioGroupMember + +func (r *RadioGroup) Add(id int, item *MenuItem) { + *r = append(*r, &RadioGroupMember{ + ID: id, + MenuItem: item, + }) +} + +func (r *RadioGroup) Bounds() (int, int) { + p := *r + return p[0].ID, p[len(p)-1].ID +} + +func (r *RadioGroup) MenuID(item *MenuItem) int { + for _, member := range *r { + if member.MenuItem == item { + return member.ID + } + } + panic("RadioGroup.MenuID: item not found:") +} + +type Win32Menu struct { + isPopup bool + menu w32.HMENU + parentWindow *windowsWebviewWindow + parent w32.HWND + menuMapping map[int]*MenuItem + checkboxItems map[*MenuItem][]int + radioGroups map[*MenuItem][]*RadioGroup + menuData *Menu + currentMenuID int + onMenuClose func() + onMenuOpen func() +} + +func (p *Win32Menu) newMenu() w32.HMENU { + if p.isPopup { + return w32.NewPopupMenu() + } + return w32.CreateMenu() +} + +func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) { + currentRadioGroup := RadioGroup{} + for _, item := range inputMenu.items { + p.currentMenuID++ + itemID := p.currentMenuID + p.menuMapping[itemID] = item + + menuItemImpl := newMenuItemImpl(item, parentMenu, itemID) + menuItemImpl.parent = inputMenu + item.impl = menuItemImpl + + if item.Hidden() { + if item.accelerator != nil { + if p.parentWindow != nil { + // Remove the accelerator from the keybindings + p.parentWindow.parent.removeMenuBinding(item.accelerator) + } else { + // Remove the global keybindings + globalApplication.KeyBinding.Remove(item.accelerator.String()) + } + } + } + + flags := uint32(w32.MF_STRING) + if item.disabled { + flags = flags | w32.MF_GRAYED + } + if item.checked { + flags = flags | w32.MF_CHECKED + } + if item.IsSeparator() { + flags = flags | w32.MF_SEPARATOR + } + + if item.checked && item.IsRadio() { + flags = flags | w32.MFT_RADIOCHECK + } + + if item.IsCheckbox() { + p.checkboxItems[item] = append(p.checkboxItems[item], itemID) + } + if item.IsRadio() { + currentRadioGroup.Add(itemID, item) + } else { + if len(currentRadioGroup) > 0 { + for _, radioMember := range currentRadioGroup { + currentRadioGroup := currentRadioGroup + p.radioGroups[radioMember.MenuItem] = append(p.radioGroups[radioMember.MenuItem], ¤tRadioGroup) + } + currentRadioGroup = RadioGroup{} + } + } + + if item.submenu != nil { + flags = flags | w32.MF_POPUP + newSubmenu := p.newMenu() + p.buildMenu(newSubmenu, item.submenu) + itemID = int(newSubmenu) + menuItemImpl.submenu = newSubmenu + } + + var menuText = item.Label() + if item.accelerator != nil { + menuText = menuText + "\t" + item.accelerator.String() + if item.callback != nil { + if p.parentWindow != nil { + p.parentWindow.parent.addMenuBinding(item.accelerator, item) + } else { + globalApplication.KeyBinding.Add(item.accelerator.String(), func(w Window) { + item.handleClick() + }) + } + } + } + + // If the item is hidden, don't append + if item.Hidden() { + continue + } + + ok := w32.AppendMenu(parentMenu, flags, uintptr(itemID), w32.MustStringToUTF16Ptr(menuText)) + if !ok { + globalApplication.fatal("error adding menu item '%s'", menuText) + } + if item.bitmap != nil { + if err := w32.SetMenuIcons(parentMenu, itemID, item.bitmap, nil); err != nil { + globalApplication.fatal("error setting menu icons: %w", err) + } + } + } + if len(currentRadioGroup) > 0 { + for _, radioMember := range currentRadioGroup { + currentRadioGroup := currentRadioGroup + p.radioGroups[radioMember.MenuItem] = append(p.radioGroups[radioMember.MenuItem], ¤tRadioGroup) + } + currentRadioGroup = RadioGroup{} + } +} + +func (p *Win32Menu) Update() { + p.menu = p.newMenu() + p.menuMapping = make(map[int]*MenuItem) + p.currentMenuID = MenuItemMsgID + p.buildMenu(p.menu, p.menuData) + p.updateRadioGroups() +} + +func NewPopupMenu(parent w32.HWND, inputMenu *Menu) *Win32Menu { + result := &Win32Menu{ + isPopup: true, + parent: parent, + menuData: inputMenu, + checkboxItems: make(map[*MenuItem][]int), + radioGroups: make(map[*MenuItem][]*RadioGroup), + } + result.Update() + return result +} +func NewApplicationMenu(parent *windowsWebviewWindow, inputMenu *Menu) *Win32Menu { + result := &Win32Menu{ + parentWindow: parent, + parent: parent.hwnd, + menuData: inputMenu, + checkboxItems: make(map[*MenuItem][]int), + radioGroups: make(map[*MenuItem][]*RadioGroup), + } + result.Update() + return result +} + +func (p *Win32Menu) ShowAt(x int, y int) { + + w32.SetForegroundWindow(p.parent) + + if p.onMenuOpen != nil { + p.onMenuOpen() + } + + // Get screen dimensions to determine menu positioning + monitor := w32.MonitorFromWindow(p.parent, w32.MONITOR_DEFAULTTONEAREST) + var monitorInfo w32.MONITORINFO + monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) + if !w32.GetMonitorInfo(monitor, &monitorInfo) { + globalApplication.fatal("GetMonitorInfo failed") + } + + // Set flags to always position the menu above the cursor + menuFlags := uint32(w32.TPM_LEFTALIGN | w32.TPM_BOTTOMALIGN) + + // Check if we're close to the right edge of the screen + // If so, right-align the menu with some padding + if x > int(monitorInfo.RcWork.Right)-200 { // Assuming 200px as a reasonable menu width + menuFlags = uint32(w32.TPM_RIGHTALIGN | w32.TPM_BOTTOMALIGN) + // Add a small padding (10px) from the right edge + x = int(monitorInfo.RcWork.Right) - 10 + } + + if !w32.TrackPopupMenuEx(p.menu, menuFlags, int32(x), int32(y), p.parent, nil) { + globalApplication.fatal("TrackPopupMenu failed") + } + + if p.onMenuClose != nil { + p.onMenuClose() + } + + if !w32.PostMessage(p.parent, w32.WM_NULL, 0, 0) { + globalApplication.fatal("PostMessage failed") + } + +} + +func (p *Win32Menu) ShowAtCursor() { + x, y, ok := w32.GetCursorPos() + if ok == false { + globalApplication.fatal("GetCursorPos failed") + } + + p.ShowAt(x, y) +} + +func (p *Win32Menu) ProcessCommand(cmdMsgID int) bool { + item := p.menuMapping[cmdMsgID] + if item == nil { + return false + } + if item.IsRadio() { + if item.checked { + return true + } + item.checked = true + p.updateRadioGroup(item) + } + if item.callback != nil { + item.handleClick() + } + return true +} + +func (p *Win32Menu) Destroy() { + w32.DestroyMenu(p.menu) +} + +func (p *Win32Menu) UpdateMenuItem(item *MenuItem) { + if item.IsCheckbox() { + for _, itemID := range p.checkboxItems[item] { + var checkState uint = w32.MF_UNCHECKED + if item.checked { + checkState = w32.MF_CHECKED + } + w32.CheckMenuItem(p.menu, uintptr(itemID), checkState) + } + return + } + if item.IsRadio() && item.checked == true { + p.updateRadioGroup(item) + } +} + +func (p *Win32Menu) updateRadioGroups() { + for menuItem := range p.radioGroups { + if menuItem.checked { + p.updateRadioGroup(menuItem) + } + } +} + +func (p *Win32Menu) updateRadioGroup(item *MenuItem) { + for _, radioGroup := range p.radioGroups[item] { + thisMenuID := radioGroup.MenuID(item) + startID, endID := radioGroup.Bounds() + w32.CheckRadio(p.menu, startID, endID, thisMenuID) + + } +} + +func (p *Win32Menu) OnMenuOpen(fn func()) { + p.onMenuOpen = fn +} + +func (p *Win32Menu) OnMenuClose(fn func()) { + p.onMenuClose = fn +} diff --git a/v3/pkg/application/roles.go b/v3/pkg/application/roles.go new file mode 100644 index 000000000..78bbf2c38 --- /dev/null +++ b/v3/pkg/application/roles.go @@ -0,0 +1,164 @@ +package application + +import "runtime" + +// Heavily inspired by Electron (c) 2013-2020 Github Inc. +// Electron License: https://github.com/electron/electron/blob/master/LICENSE + +// Role is a type to identify menu roles +type Role uint + +// These constants need to be kept in sync with `v2/internal/frontend/desktop/darwin/Role.h` +const ( + NoRole Role = iota + AppMenu Role = iota + EditMenu Role = iota + ViewMenu Role = iota + WindowMenu Role = iota + ServicesMenu Role = iota + HelpMenu Role = iota + + Hide Role = iota + HideOthers Role = iota + ShowAll Role = iota + BringAllToFront Role = iota + UnHide Role = iota + About Role = iota + Undo Role = iota + Redo Role = iota + Cut Role = iota + Copy Role = iota + Paste Role = iota + PasteAndMatchStyle Role = iota + SelectAll Role = iota + Delete Role = iota + SpeechMenu Role = iota + Quit Role = iota + FileMenu Role = iota + CloseWindow Role = iota + Reload Role = iota + ForceReload Role = iota + OpenDevTools Role = iota + ResetZoom Role = iota + ZoomIn Role = iota + ZoomOut Role = iota + ToggleFullscreen Role = iota + + Minimise Role = iota + Zoom Role = iota + FullScreen Role = iota + + NewFile Role = iota + Open Role = iota + Save Role = iota + SaveAs Role = iota + StartSpeaking Role = iota + StopSpeaking Role = iota + Revert Role = iota + Print Role = iota + PageLayout Role = iota + Find Role = iota + FindAndReplace Role = iota + FindNext Role = iota + FindPrevious Role = iota + Front Role = iota + Help Role = iota +) + +func NewFileMenu() *MenuItem { + fileMenu := NewMenu() + if runtime.GOOS == "darwin" { + fileMenu.AddRole(CloseWindow) + } else { + fileMenu.AddRole(Quit) + } + subMenu := NewSubMenuItem("File") + subMenu.submenu = fileMenu + return subMenu +} + +func NewViewMenu() *MenuItem { + viewMenu := NewMenu() + viewMenu.AddRole(Reload) + viewMenu.AddRole(ForceReload) + addDevToolMenuItem(viewMenu) + viewMenu.AddSeparator() + viewMenu.AddRole(ResetZoom) + viewMenu.AddRole(ZoomIn) + viewMenu.AddRole(ZoomOut) + viewMenu.AddSeparator() + viewMenu.AddRole(ToggleFullscreen) + subMenu := NewSubMenuItem("View") + subMenu.submenu = viewMenu + return subMenu +} + +func NewAppMenu() *MenuItem { + if runtime.GOOS != "darwin" { + return nil + } + appMenu := NewMenu() + appMenu.AddRole(About) + appMenu.AddSeparator() + appMenu.AddRole(ServicesMenu) + appMenu.AddSeparator() + appMenu.AddRole(Hide) + appMenu.AddRole(HideOthers) + appMenu.AddRole(UnHide) + appMenu.AddSeparator() + appMenu.AddRole(Quit) + subMenu := NewSubMenuItem(globalApplication.options.Name) + subMenu.submenu = appMenu + return subMenu +} + +func NewEditMenu() *MenuItem { + editMenu := NewMenu() + editMenu.AddRole(Undo) + editMenu.AddRole(Redo) + editMenu.AddSeparator() + editMenu.AddRole(Cut) + editMenu.AddRole(Copy) + editMenu.AddRole(Paste) + if runtime.GOOS == "darwin" { + editMenu.AddRole(PasteAndMatchStyle) + editMenu.AddRole(Delete) + editMenu.AddRole(SelectAll) + editMenu.AddSeparator() + editMenu.AddRole(SpeechMenu) + } else { + editMenu.AddRole(Delete) + editMenu.AddSeparator() + editMenu.AddRole(SelectAll) + } + subMenu := NewSubMenuItem("Edit") + subMenu.submenu = editMenu + return subMenu +} + +func NewWindowMenu() *MenuItem { + menu := NewMenu() + menu.AddRole(Minimise) + menu.AddRole(Zoom) + if runtime.GOOS == "darwin" { + menu.AddSeparator() + menu.AddRole(Front) + //menu.AddSeparator() + //menu.AddRole(Window) + } else { + menu.AddRole(CloseWindow) + } + subMenu := NewSubMenuItem("Window") + subMenu.submenu = menu + return subMenu +} + +func NewHelpMenu() *MenuItem { + menu := NewMenu() + menu.Add("Learn More").OnClick(func(ctx *Context) { + globalApplication.Window.Current().SetURL("https://wails.io") + }) + subMenu := NewSubMenuItem("Help") + subMenu.submenu = menu + return subMenu +} diff --git a/v3/pkg/application/roles_dev.go b/v3/pkg/application/roles_dev.go new file mode 100644 index 000000000..1c03e398d --- /dev/null +++ b/v3/pkg/application/roles_dev.go @@ -0,0 +1,7 @@ +//go:build !production || devtools + +package application + +func addDevToolMenuItem(viewMenu *Menu) { + viewMenu.AddRole(OpenDevTools) +} diff --git a/v3/pkg/application/roles_production.go b/v3/pkg/application/roles_production.go new file mode 100644 index 000000000..d1af8dba7 --- /dev/null +++ b/v3/pkg/application/roles_production.go @@ -0,0 +1,5 @@ +//go:build production && !devtools + +package application + +func addDevToolMenuItem(viewMenu *Menu) {} diff --git a/v3/pkg/application/screen_darwin.go b/v3/pkg/application/screen_darwin.go new file mode 100644 index 000000000..ad5285e34 --- /dev/null +++ b/v3/pkg/application/screen_darwin.go @@ -0,0 +1,194 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit -framework AppKit +#import +#import +#import +#import +#include + +typedef struct Screen { + const char* id; + const char* name; + int p_width; + int p_height; + int width; + int height; + int x; + int y; + int w_width; + int w_height; + int w_x; + int w_y; + float scaleFactor; + double rotation; + bool isPrimary; +} Screen; + + +int GetNumScreens(){ + return [[NSScreen screens] count]; +} + +Screen processScreen(NSScreen* screen){ + Screen returnScreen; + returnScreen.scaleFactor = screen.backingScaleFactor; + + // screen bounds + returnScreen.height = screen.frame.size.height; + returnScreen.width = screen.frame.size.width; + returnScreen.x = screen.frame.origin.x; + returnScreen.y = screen.frame.origin.y; + + // work area + NSRect workArea = [screen visibleFrame]; + returnScreen.w_height = workArea.size.height; + returnScreen.w_width = workArea.size.width; + returnScreen.w_x = workArea.origin.x; + returnScreen.w_y = workArea.origin.y; + + + // adapted from https://stackoverflow.com/a/1237490/4188138 + NSDictionary* screenDictionary = [screen deviceDescription]; + NSNumber* screenID = [screenDictionary objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID displayID = [screenID unsignedIntValue]; + returnScreen.id = [[NSString stringWithFormat:@"%d", displayID] UTF8String]; + + // Get physical monitor size + NSValue *sizeValue = [screenDictionary objectForKey:@"NSDeviceSize"]; + NSSize physicalSize = sizeValue.sizeValue; + returnScreen.p_height = physicalSize.height; + returnScreen.p_width = physicalSize.width; + + // Get the rotation + double rotation = CGDisplayRotation(displayID); + returnScreen.rotation = rotation; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 + if( @available(macOS 10.15, *) ){ + returnScreen.name = [screen.localizedName UTF8String]; + } +#endif + return returnScreen; +} + +// Get primary screen +Screen GetPrimaryScreen(){ + // Get primary screen + NSScreen *mainScreen = [NSScreen mainScreen]; + return processScreen(mainScreen); +} + +Screen* getAllScreens() { + NSArray *screens = [NSScreen screens]; + Screen* returnScreens = malloc(sizeof(Screen) * screens.count); + for (int i = 0; i < screens.count; i++) { + NSScreen* screen = [screens objectAtIndex:i]; + returnScreens[i] = processScreen(screen); + } + return returnScreens; +} + +Screen getScreenForWindow(void* window){ + NSScreen* screen = ((NSWindow*)window).screen; + return processScreen(screen); +} + +// Get the screen for the system tray +Screen getScreenForSystemTray(void* nsStatusItem) { + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + NSRect frame = statusItem.button.frame; + NSArray *screens = NSScreen.screens; + NSScreen *associatedScreen = nil; + + for (NSScreen *screen in screens) { + if (NSPointInRect(frame.origin, screen.frame)) { + associatedScreen = screen; + break; + } + } + return processScreen(associatedScreen); +} + +void* getWindowForSystray(void* nsStatusItem) { + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + return statusItem.button.window; +} + + +*/ +import "C" +import "unsafe" + +func cScreenToScreen(screen C.Screen) *Screen { + + return &Screen{ + Size: Size{ + Width: int(screen.p_width), + Height: int(screen.p_height), + }, + Bounds: Rect{ + X: int(screen.x), + Y: int(screen.y), + Height: int(screen.height), + Width: int(screen.width), + }, + PhysicalBounds: Rect{ + X: int(screen.x), + Y: int(screen.y), + Height: int(screen.height), + Width: int(screen.width), + }, + WorkArea: Rect{ + X: int(screen.w_x), + Y: int(screen.w_y), + Height: int(screen.w_height), + Width: int(screen.w_width), + }, + PhysicalWorkArea: Rect{ + X: int(screen.w_x), + Y: int(screen.w_y), + Height: int(screen.w_height), + Width: int(screen.w_width), + }, + ScaleFactor: float32(screen.scaleFactor), + ID: C.GoString(screen.id), + Name: C.GoString(screen.name), + IsPrimary: bool(screen.isPrimary), + Rotation: float32(screen.rotation), + } +} + +func (m *macosApp) getPrimaryScreen() (*Screen, error) { + cScreen := C.GetPrimaryScreen() + return cScreenToScreen(cScreen), nil +} + +func (m *macosApp) getScreens() ([]*Screen, error) { + cScreens := C.getAllScreens() + defer C.free(unsafe.Pointer(cScreens)) + numScreens := int(C.GetNumScreens()) + displays := make([]*Screen, numScreens) + cScreenHeaders := (*[1 << 30]C.Screen)(unsafe.Pointer(cScreens))[:numScreens:numScreens] + for i := 0; i < numScreens; i++ { + displays[i] = cScreenToScreen(cScreenHeaders[i]) + } + return displays, nil +} + +func getScreenForWindow(window *macosWebviewWindow) (*Screen, error) { + cScreen := C.getScreenForWindow(window.nsWindow) + return cScreenToScreen(cScreen), nil +} + +func getScreenForSystray(systray *macosSystemTray) (*Screen, error) { + // Get the Window for the status item + // https://stackoverflow.com/a/5875019/4188138 + window := C.getWindowForSystray(systray.nsStatusItem) + cScreen := C.getScreenForWindow(window) + return cScreenToScreen(cScreen), nil +} diff --git a/v3/pkg/application/screen_linux.go b/v3/pkg/application/screen_linux.go new file mode 100644 index 000000000..f6dfdf59d --- /dev/null +++ b/v3/pkg/application/screen_linux.go @@ -0,0 +1,37 @@ +//go:build linux + +package application + +import ( + "sync" +) + +func (a *linuxApp) getPrimaryScreen() (*Screen, error) { + var wg sync.WaitGroup + var screen *Screen + var err error + wg.Add(1) + InvokeSync(func() { + screen, err = getPrimaryScreen() + wg.Done() + }) + wg.Wait() + return screen, err +} + +func (a *linuxApp) getScreens() ([]*Screen, error) { + var wg sync.WaitGroup + var screens []*Screen + var err error + wg.Add(1) + InvokeSync(func() { + screens, err = getScreens(a.application) + wg.Done() + }) + wg.Wait() + return screens, err +} + +func getScreenForWindow(window *linuxWebviewWindow) (*Screen, error) { + return window.getScreen() +} diff --git a/v3/pkg/application/screen_windows.go b/v3/pkg/application/screen_windows.go new file mode 100644 index 000000000..b6328f809 --- /dev/null +++ b/v3/pkg/application/screen_windows.go @@ -0,0 +1,88 @@ +//go:build windows + +package application + +import ( + "errors" + "strconv" + + "github.com/wailsapp/wails/v3/pkg/w32" + "golang.org/x/sys/windows" +) + +func (m *windowsApp) processAndCacheScreens() error { + allScreens, err := w32.GetAllScreens() + if err != nil { + return err + } + + // Convert result to []*Screen + var screens []*Screen + + for _, screen := range allScreens { + x := int(screen.MONITORINFOEX.RcMonitor.Left) + y := int(screen.MONITORINFOEX.RcMonitor.Top) + right := int(screen.MONITORINFOEX.RcMonitor.Right) + bottom := int(screen.MONITORINFOEX.RcMonitor.Bottom) + width := right - x + height := bottom - y + + workArea := Rect{ + X: int(screen.MONITORINFOEX.RcWork.Left), + Y: int(screen.MONITORINFOEX.RcWork.Top), + Width: int(screen.MONITORINFOEX.RcWork.Right - screen.MONITORINFOEX.RcWork.Left), + Height: int(screen.MONITORINFOEX.RcWork.Bottom - screen.MONITORINFOEX.RcWork.Top), + } + + screens = append(screens, &Screen{ + ID: hMonitorToScreenID(screen.HMonitor), + Name: windows.UTF16ToString(screen.MONITORINFOEX.SzDevice[:]), + X: x, + Y: y, + Size: Size{Width: width, Height: height}, + Bounds: Rect{X: x, Y: y, Width: width, Height: height}, + PhysicalBounds: Rect{X: x, Y: y, Width: width, Height: height}, + WorkArea: workArea, + PhysicalWorkArea: workArea, + IsPrimary: screen.IsPrimary, + ScaleFactor: screen.ScaleFactor, + Rotation: 0, + }) + } + + err = m.parent.Screen.LayoutScreens(screens) + if err != nil { + return err + } + + return nil +} + +// NOTE: should be moved to *App after DPI is implemented in all platforms +func (m *windowsApp) getScreens() ([]*Screen, error) { + return m.parent.Screen.screens, nil +} + +// NOTE: should be moved to *App after DPI is implemented in all platforms +func (m *windowsApp) getPrimaryScreen() (*Screen, error) { + return m.parent.Screen.primaryScreen, nil +} + +func getScreenForWindow(window *windowsWebviewWindow) (*Screen, error) { + return ScreenNearestPhysicalRect(window.physicalBounds()), nil +} + +func getScreenForWindowHwnd(hwnd w32.HWND) (*Screen, error) { + hMonitor := w32.MonitorFromWindow(hwnd, w32.MONITOR_DEFAULTTONEAREST) + screenID := hMonitorToScreenID(hMonitor) + for _, screen := range globalApplication.Screen.screens { + if screen.ID == screenID { + return screen, nil + } + } + return nil, errors.New("screen not found for window") +} + +func hMonitorToScreenID(hMonitor uintptr) string { + return strconv.Itoa(int(hMonitor)) +} diff --git a/v3/pkg/application/screenmanager.go b/v3/pkg/application/screenmanager.go new file mode 100644 index 000000000..6caa1cd33 --- /dev/null +++ b/v3/pkg/application/screenmanager.go @@ -0,0 +1,875 @@ +package application + +import ( + "errors" + "math" + "sort" +) + +// Heavily inspired by the Chromium project (Copyright 2015 The Chromium Authors) +// Chromium License: https://chromium.googlesource.com/chromium/src/+/HEAD/LICENSE + +type ScreenManager struct { + app *App + screens []*Screen + primaryScreen *Screen +} + +// newScreenManager creates a new ScreenManager instance +func newScreenManager(app *App) *ScreenManager { + return &ScreenManager{ + app: app, + } +} + +type Screen struct { + ID string // A unique identifier for the display + Name string // The name of the display + ScaleFactor float32 // The scale factor of the display (DPI/96) + X int // The x-coordinate of the top-left corner of the rectangle + Y int // The y-coordinate of the top-left corner of the rectangle + Size Size // The size of the display + Bounds Rect // The bounds of the display + PhysicalBounds Rect // The physical bounds of the display (before scaling) + WorkArea Rect // The work area of the display + PhysicalWorkArea Rect // The physical work area of the display (before scaling) + IsPrimary bool // Whether this is the primary display + Rotation float32 // The rotation of the display +} + +type Rect struct { + X int + Y int + Width int + Height int +} + +type Point struct { + X int + Y int +} +type Size struct { + Width int + Height int +} + +type Alignment int +type OffsetReference int + +const ( + TOP Alignment = iota + RIGHT + BOTTOM + LEFT +) + +const ( + BEGIN OffsetReference = iota // TOP or LEFT + END // BOTTOM or RIGHT +) + +// ScreenPlacement specifies where the screen (S) is placed relative to +// parent (P) screen. In the following example, (S) is RIGHT aligned to (P) +// with a positive offset and a BEGIN (top) offset reference. +// +// . +------------+ + +// . | | | offset +// . | P | v +// . | +--------+ +// . | | | +// . +------------+ S | +// . | | +// . +--------+ +type ScreenPlacement struct { + screen *Screen + parent *Screen + alignment Alignment + offset int + offsetReference OffsetReference +} + +func (r Rect) Origin() Point { + return Point{ + X: r.X, + Y: r.Y, + } +} + +func (s Screen) Origin() Point { + return Point{ + X: s.X, + Y: s.Y, + } +} + +func (r Rect) Corner() Point { + return Point{ + X: r.right(), + Y: r.bottom(), + } +} + +func (r Rect) InsideCorner() Point { + return Point{ + X: r.right() - 1, + Y: r.bottom() - 1, + } +} + +func (r Rect) right() int { + return r.X + r.Width +} + +func (r Rect) bottom() int { + return r.Y + r.Height +} + +func (s Screen) right() int { + return s.Bounds.right() +} + +func (s Screen) bottom() int { + return s.Bounds.bottom() +} + +func (s Screen) scale(value int, toDip bool) int { + // Round up when scaling down and round down when scaling up. + // This mix rounding strategy prevents drift over time when applying multiple scaling back and forth. + // In addition, It has been shown that using this approach minimized rounding issues and improved overall + // precision when converting between DIP and physical coordinates. + if toDip { + return int(math.Ceil(float64(value) / float64(s.ScaleFactor))) + } else { + return int(math.Floor(float64(value) * float64(s.ScaleFactor))) + } +} + +func (r Rect) Size() Size { + return Size{ + Width: r.Width, + Height: r.Height, + } +} + +func (r Rect) IsEmpty() bool { + return r.Width <= 0 || r.Height <= 0 +} + +func (r Rect) Contains(pt Point) bool { + return pt.X >= r.X && pt.X < r.X+r.Width && pt.Y >= r.Y && pt.Y < r.Y+r.Height +} + +// Get intersection with another rect +func (r Rect) Intersect(otherRect Rect) Rect { + if r.IsEmpty() || otherRect.IsEmpty() { + return Rect{} + } + + maxLeft := max(r.X, otherRect.X) + maxTop := max(r.Y, otherRect.Y) + minRight := min(r.right(), otherRect.right()) + minBottom := min(r.bottom(), otherRect.bottom()) + + if minRight > maxLeft && minBottom > maxTop { + return Rect{ + X: maxLeft, + Y: maxTop, + Width: minRight - maxLeft, + Height: minBottom - maxTop, + } + } + return Rect{} +} + +// Check if screens intersects another screen +func (s *Screen) intersects(otherScreen *Screen) bool { + maxLeft := max(s.X, otherScreen.X) + maxTop := max(s.Y, otherScreen.Y) + minRight := min(s.right(), otherScreen.right()) + minBottom := min(s.bottom(), otherScreen.bottom()) + + return minRight > maxLeft && minBottom > maxTop +} + +// Get distance from another rect (squared) +func (r Rect) distanceFromRectSquared(otherRect Rect) int { + // If they intersect, return negative area of intersection + intersection := r.Intersect(otherRect) + if !intersection.IsEmpty() { + return -(intersection.Width * intersection.Height) + } + + dX := max(0, max(r.X-otherRect.right(), otherRect.X-r.right())) + dY := max(0, max(r.Y-otherRect.bottom(), otherRect.Y-r.bottom())) + + // Distance squared + return dX*dX + dY*dY +} + +// Apply screen placement +func (p ScreenPlacement) apply() { + parentBounds := p.parent.Bounds + screenBounds := p.screen.Bounds + + newX := parentBounds.X + newY := parentBounds.Y + offset := p.offset + + if p.alignment == TOP || p.alignment == BOTTOM { + if p.offsetReference == END { + offset = parentBounds.Width - offset - screenBounds.Width + } + offset = min(offset, parentBounds.Width) + offset = max(offset, -screenBounds.Width) + newX += offset + if p.alignment == TOP { + newY -= screenBounds.Height + } else { + newY += parentBounds.Height + } + } else { + if p.offsetReference == END { + offset = parentBounds.Height - offset - screenBounds.Height + } + offset = min(offset, parentBounds.Height) + offset = max(offset, -screenBounds.Height) + newY += offset + if p.alignment == LEFT { + newX -= screenBounds.Width + } else { + newX += parentBounds.Width + } + } + + p.screen.move(newX, newY) +} + +func (s *Screen) absoluteToRelativeDipPoint(dipPoint Point) Point { + return Point{ + X: dipPoint.X - s.Bounds.X, + Y: dipPoint.Y - s.Bounds.Y, + } +} + +func (s *Screen) relativeToAbsoluteDipPoint(dipPoint Point) Point { + return Point{ + X: dipPoint.X + s.Bounds.X, + Y: dipPoint.Y + s.Bounds.Y, + } +} + +func (s *Screen) absoluteToRelativePhysicalPoint(physicalPoint Point) Point { + return Point{ + X: physicalPoint.X - s.PhysicalBounds.X, + Y: physicalPoint.Y - s.PhysicalBounds.Y, + } +} + +func (s *Screen) relativeToAbsolutePhysicalPoint(physicalPoint Point) Point { + return Point{ + X: physicalPoint.X + s.PhysicalBounds.X, + Y: physicalPoint.Y + s.PhysicalBounds.Y, + } +} + +func (s *Screen) move(newX, newY int) { + workAreaOffsetX := s.WorkArea.X - s.X + workAreaOffsetY := s.WorkArea.Y - s.Y + + s.X = newX + s.Y = newY + s.Bounds.X = newX + s.Bounds.Y = newY + s.WorkArea.X = newX + workAreaOffsetX + s.WorkArea.Y = newY + workAreaOffsetY +} + +func (s *Screen) applyDPIScaling() { + if s.ScaleFactor == 1 { + return + } + workAreaOffsetX := s.WorkArea.X - s.Bounds.X + workAreaOffsetY := s.WorkArea.Y - s.Bounds.Y + + s.WorkArea.X = s.Bounds.X + s.scale(workAreaOffsetX, true) + s.WorkArea.Y = s.Bounds.Y + s.scale(workAreaOffsetY, true) + + s.Bounds.Width = s.scale(s.PhysicalBounds.Width, true) + s.Bounds.Height = s.scale(s.PhysicalBounds.Height, true) + s.WorkArea.Width = s.scale(s.PhysicalWorkArea.Width, true) + s.WorkArea.Height = s.scale(s.PhysicalWorkArea.Height, true) + + s.Size.Width = s.Bounds.Width + s.Size.Height = s.Bounds.Height +} + +func (s *Screen) dipToPhysicalPoint(dipPoint Point, isCorner bool) Point { + relativePoint := s.absoluteToRelativeDipPoint(dipPoint) + scaledRelativePoint := Point{ + X: s.scale(relativePoint.X, false), + Y: s.scale(relativePoint.Y, false), + } + // Align edge points (fixes rounding issues) + edgeOffset := 1 + if isCorner { + edgeOffset = 0 + } + if relativePoint.X == s.Bounds.Width-edgeOffset { + scaledRelativePoint.X = s.PhysicalBounds.Width - edgeOffset + } + if relativePoint.Y == s.Bounds.Height-edgeOffset { + scaledRelativePoint.Y = s.PhysicalBounds.Height - edgeOffset + } + return s.relativeToAbsolutePhysicalPoint(scaledRelativePoint) +} + +func (s *Screen) physicalToDipPoint(physicalPoint Point, isCorner bool) Point { + relativePoint := s.absoluteToRelativePhysicalPoint(physicalPoint) + scaledRelativePoint := Point{ + X: s.scale(relativePoint.X, true), + Y: s.scale(relativePoint.Y, true), + } + // Align edge points (fixes rounding issues) + edgeOffset := 1 + if isCorner { + edgeOffset = 0 + } + if relativePoint.X == s.PhysicalBounds.Width-edgeOffset { + scaledRelativePoint.X = s.Bounds.Width - edgeOffset + } + if relativePoint.Y == s.PhysicalBounds.Height-edgeOffset { + scaledRelativePoint.Y = s.Bounds.Height - edgeOffset + } + return s.relativeToAbsoluteDipPoint(scaledRelativePoint) +} + +func (s *Screen) dipToPhysicalRect(dipRect Rect) Rect { + origin := s.dipToPhysicalPoint(dipRect.Origin(), false) + corner := s.dipToPhysicalPoint(dipRect.Corner(), true) + + return Rect{ + X: origin.X, + Y: origin.Y, + Width: corner.X - origin.X, + Height: corner.Y - origin.Y, + } +} + +func (s *Screen) physicalToDipRect(physicalRect Rect) Rect { + origin := s.physicalToDipPoint(physicalRect.Origin(), false) + corner := s.physicalToDipPoint(physicalRect.Corner(), true) + + return Rect{ + X: origin.X, + Y: origin.Y, + Width: corner.X - origin.X, + Height: corner.Y - origin.Y, + } +} + +// Layout screens in the virtual space with DIP calculations and cache the screens +// for future coordinate transformation between the physical and logical (DIP) space +func (m *ScreenManager) LayoutScreens(screens []*Screen) error { + if screens == nil || len(screens) == 0 { + return errors.New("screens parameter is nil or empty") + } + m.screens = screens + + err := m.calculateScreensDipCoordinates() + if err != nil { + return err + } + + return nil +} + +func (m *ScreenManager) GetAll() []*Screen { + return m.screens +} + +func (m *ScreenManager) GetPrimary() *Screen { + return m.primaryScreen +} + +// Reference: https://source.chromium.org/chromium/chromium/src/+/main:ui/display/win/screen_win.cc;l=317 +func (m *ScreenManager) calculateScreensDipCoordinates() error { + remainingScreens := []*Screen{} + + // Find the primary screen + m.primaryScreen = nil + for _, screen := range m.screens { + if screen.IsPrimary { + m.primaryScreen = screen + } else { + remainingScreens = append(remainingScreens, screen) + } + } + if m.primaryScreen == nil { + return errors.New("no primary screen found") + } else if len(remainingScreens) != len(m.screens)-1 { + return errors.New("invalid primary screen found") + } + + // Build screens tree using the primary screen as root + screensPlacements := []ScreenPlacement{} + availableParents := []*Screen{m.primaryScreen} + for len(availableParents) > 0 { + // Pop a parent + end := len(availableParents) - 1 + parent := availableParents[end] + availableParents = availableParents[:end] + // Find touching screens + for _, child := range m.findAndRemoveTouchingScreens(parent, &remainingScreens) { + screenPlacement := m.calculateScreenPlacement(child, parent) + screensPlacements = append(screensPlacements, screenPlacement) + availableParents = append(availableParents, child) + } + } + + // Apply screens DPI scaling and placement starting with + // the primary screen and then dependent screens + m.primaryScreen.applyDPIScaling() + for _, placement := range screensPlacements { + placement.screen.applyDPIScaling() + placement.apply() + } + + // Now that all the placements have been applied, + // we must detect and fix any overlapping screens. + m.deIntersectScreens(screensPlacements) + + return nil +} + +// Returns a ScreenPlacement for |screen| relative to |parent|. +// Note that ScreenPlacement's are always in DIPs, so this also performs the +// required scaling. +// References: +// - https://github.com/chromium/chromium/blob/main/ui/display/win/scaling_util.h#L25 +// - https://github.com/chromium/chromium/blob/main/ui/display/win/scaling_util.cc#L142 +func (m *ScreenManager) calculateScreenPlacement(screen, parent *Screen) ScreenPlacement { + // Examples (The offset is indicated by the arrow.): + // Scaled and Unscaled Coordinates + // +--------------+ + Since both screens are of the same scale + // | | | factor, relative positions remain the same. + // | Parent | V + // | 1x +----------+ + // | | | + // +--------------+ Screen | + // | 1x | + // +----------+ + // + // Unscaled Coordinates + // +--------------+ The 2x screen is offset to maintain a + // | | similar neighboring relationship with the 1x + // | Parent | parent. Screen's position is based off of the + // | 1x +----------+ percentage position along its parent. This + // | | | percentage position is preserved in the scaled + // +--------------+ Screen | coordinates. + // | 2x | + // +----------+ + // Scaled Coordinates + // +--------------+ + + // | | | + // | Parent | V + // | 1x +-----+ + // | | S 2x| + // +--------------+-----+ + // + // + // Unscaled Coordinates + // +--------------+ The parent screen has a 2x scale factor. + // | | The offset is adjusted to maintain the + // | | relative positioning of the 1x screen in + // | Parent +----------+ the scaled coordinate space. Screen's + // | 2x | | position is based off of the percentage + // | | Screen | position along its parent. This percentage + // | | 1x | position is preserved in the scaled + // +--------------+ | coordinates. + // | | + // +----------+ + // Scaled Coordinates + // +-------+ + + // | | V + // | Parent+----------+ + // | 2x | | + // +-------+ Screen | + // | 1x | + // | | + // | | + // +----------+ + // + // Unscaled Coordinates + // +----------+ In this case, parent lies between the top and + // | | bottom of parent. The roles are reversed when + // +-------+ | this occurs, and screen is placed to maintain + // | | Screen | parent's relative position along screen. + // | Parent| 1x | + // | 2x | | + // +-------+ | + // +----------+ + // Scaled Coordinates + // ^ +----------+ + // | | | + // + +----+ | + // |Prnt| Screen | + // | 2x | 1x | + // +----+ | + // | | + // +----------+ + // + // Scaled and Unscaled Coordinates + // +--------+ If the two screens are bottom aligned or + // | | right aligned, the ScreenPlacement will + // | +--------+ have an offset of 0 relative to the + // | | | end of the screen. + // | | | + // +--------+--------+ + + placement := ScreenPlacement{ + screen: screen, + parent: parent, + alignment: m.getScreenAlignment(screen, parent), + offset: 0, + offsetReference: BEGIN, + } + + screenBegin, screenEnd := 0, 0 + parentBegin, parentEnd := 0, 0 + + switch placement.alignment { + case TOP, BOTTOM: + screenBegin = screen.X + screenEnd = screen.right() + parentBegin = parent.X + parentEnd = parent.right() + case LEFT, RIGHT: + screenBegin = screen.Y + screenEnd = screen.bottom() + parentBegin = parent.Y + parentEnd = parent.bottom() + } + + // Since we're calculating offsets, make everything relative to parentBegin + parentEnd -= parentBegin + screenBegin -= parentBegin + screenEnd -= parentBegin + parentBegin = 0 + + // There are a few ways lines can intersect: + // End Aligned + // SCREEN's offset is relative to the END (BOTTOM or RIGHT). + // +-PARENT----------------+ + // +-SCREEN-------------+ + // + // Positioning based off of |screenBegin|. + // SCREEN's offset is simply a percentage of its position on PARENT. + // +-PARENT----------------+ + // ^+-SCREEN------------+ + // + // Positioning based off of |screenEnd|. + // SCREEN's offset is dependent on the percentage of its end position on PARENT. + // +-PARENT----------------+ + // +-SCREEN------------+^ + // + // Positioning based off of |parentBegin| on SCREEN. + // SCREEN's offset is dependent on the percentage of its position on PARENT. + // +-PARENT----------------+ + // ^+-SCREEN--------------------------+ + + if screenEnd == parentEnd { + placement.offsetReference = END + placement.offset = 0 + } else if screenBegin >= parentBegin { + placement.offsetReference = BEGIN + placement.offset = m.scaleOffset(parentEnd, parent.ScaleFactor, screenBegin) + } else if screenEnd <= parentEnd { + placement.offsetReference = END + placement.offset = m.scaleOffset(parentEnd, parent.ScaleFactor, parentEnd-screenEnd) + } else { + placement.offsetReference = BEGIN + placement.offset = m.scaleOffset(screenEnd-screenBegin, screen.ScaleFactor, screenBegin) + } + + return placement +} + +// Get screen alignment relative to parent (TOP, RIGHT, BOTTOM, LEFT) +func (m *ScreenManager) getScreenAlignment(screen, parent *Screen) Alignment { + maxLeft := max(screen.X, parent.X) + maxTop := max(screen.Y, parent.Y) + minRight := min(screen.right(), parent.right()) + minBottom := min(screen.bottom(), parent.bottom()) + + // Corners touching + if maxLeft == minRight && maxTop == minBottom { + if screen.Y == maxTop { + return BOTTOM + } else if parent.X == maxLeft { + return LEFT + } + return TOP + } + + // Vertical edge touching + if maxLeft == minRight { + if screen.X == maxLeft { + return RIGHT + } else { + return LEFT + } + } + + // Horizontal edge touching + if maxTop == minBottom { + if screen.Y == maxTop { + return BOTTOM + } else { + return TOP + } + } + + return -1 // Shouldn't be reached +} + +func (m *ScreenManager) deIntersectScreens(screensPlacements []ScreenPlacement) { + parentIDMap := make(map[string]string) + for _, placement := range screensPlacements { + parentIDMap[placement.screen.ID] = placement.parent.ID + } + + treeDepthMap := make(map[string]int) + for _, screen := range m.screens { + id, ok, depth := screen.ID, true, 0 + const maxDepth = 100 + for id != m.primaryScreen.ID && depth < maxDepth { + depth++ + id, ok = parentIDMap[id] + if !ok { + depth = maxDepth + } + } + treeDepthMap[screen.ID] = depth + } + + sortedScreens := make([]*Screen, len(m.screens)) + copy(sortedScreens, m.screens) + + // Sort the screens first by their depth in the screen hierarchy tree, + // and then by distance from screen origin to primary origin. This way we + // process the screens starting at the root (the primary screen), in the + // order of their descendance spanning out from the primary screen. + sort.Slice(sortedScreens, func(i, j int) bool { + s1, s2 := m.screens[i], m.screens[j] + s1_depth := treeDepthMap[s1.ID] + s2_depth := treeDepthMap[s2.ID] + + if s1_depth != s2_depth { + return s1_depth < s2_depth + } + + // Distance squared + s1_distance := s1.X*s1.X + s1.Y*s1.Y + s2_distance := s2.X*s2.X + s2.Y*s2.Y + if s1_distance != s2_distance { + return s1_distance < s2_distance + } + + return s1.ID < s2.ID + }) + + for i := 1; i < len(sortedScreens); i++ { + targetScreen := sortedScreens[i] + for j := 0; j < i; j++ { + sourceScreen := sortedScreens[j] + if targetScreen.intersects(sourceScreen) { + m.fixScreenIntersection(targetScreen, sourceScreen) + } + } + } +} + +// Offset the target screen along either X or Y axis away from the origin +// so that it removes the intersection with the source screen +// This function assume both screens already intersect. +func (m *ScreenManager) fixScreenIntersection(targetScreen, sourceScreen *Screen) { + offsetX, offsetY := 0, 0 + + if targetScreen.X >= 0 { + offsetX = sourceScreen.right() - targetScreen.X + } else { + offsetX = -(targetScreen.right() - sourceScreen.X) + } + + if targetScreen.Y >= 0 { + offsetY = sourceScreen.bottom() - targetScreen.Y + } else { + offsetY = -(targetScreen.bottom() - sourceScreen.Y) + } + + // Choose the smaller offset (X or Y) + if math.Abs(float64(offsetX)) <= math.Abs(float64(offsetY)) { + offsetY = 0 + } else { + offsetX = 0 + } + + // Apply the offset + newX := targetScreen.X + offsetX + newY := targetScreen.Y + offsetY + targetScreen.move(newX, newY) +} + +func (m *ScreenManager) findAndRemoveTouchingScreens(parent *Screen, screens *[]*Screen) []*Screen { + touchingScreens := []*Screen{} + remainingScreens := []*Screen{} + + for _, screen := range *screens { + if m.areScreensTouching(parent, screen) { + touchingScreens = append(touchingScreens, screen) + } else { + remainingScreens = append(remainingScreens, screen) + } + } + *screens = remainingScreens + return touchingScreens +} + +func (m *ScreenManager) areScreensTouching(a, b *Screen) bool { + maxLeft := max(a.X, b.X) + maxTop := max(a.Y, b.Y) + minRight := min(a.right(), b.right()) + minBottom := min(a.bottom(), b.bottom()) + return (maxLeft == minRight && maxTop <= minBottom) || (maxTop == minBottom && maxLeft <= minRight) +} + +// Scale |unscaledOffset| to the same relative position on |unscaledLength| +// based off of |unscaledLength|'s |scaleFactor| +func (m *ScreenManager) scaleOffset(unscaledLength int, scaleFactor float32, unscaledOffset int) int { + scaledLength := float32(unscaledLength) / scaleFactor + percent := float32(unscaledOffset) / float32(unscaledLength) + return int(math.Floor(float64(scaledLength * percent))) +} + +func (m *ScreenManager) screenNearestPoint(point Point, isPhysical bool) *Screen { + for _, screen := range m.screens { + if isPhysical { + if screen.PhysicalBounds.Contains(point) { + return screen + } + } else { + if screen.Bounds.Contains(point) { + return screen + } + } + } + return m.primaryScreen +} + +func (m *ScreenManager) screenNearestRect(rect Rect, isPhysical bool, excludedScreens map[string]bool) *Screen { + var nearestScreen *Screen + var distance, nearestScreenDistance int + for _, screen := range m.screens { + if excludedScreens[screen.ID] { + continue + } + if isPhysical { + distance = rect.distanceFromRectSquared(screen.PhysicalBounds) + } else { + distance = rect.distanceFromRectSquared(screen.Bounds) + } + if nearestScreen == nil || distance < nearestScreenDistance { + nearestScreen = screen + nearestScreenDistance = distance + } + } + if !isPhysical && len(excludedScreens) < len(m.screens)-1 { + // Make sure to give the same screen that would be given by the physical rect + // of this dip rect so transforming back and forth always gives the same result. + // This is important because it could happen that a dip rect intersects Screen1 + // more than Screen2 but in the physical layout Screen2 will scale up or Screen1 + // will scale down causing the intersection area to change so transforming back + // would give a different rect. + physicalRect := nearestScreen.dipToPhysicalRect(rect) + physicalRectScreen := m.screenNearestRect(physicalRect, true, nil) + if nearestScreen != physicalRectScreen { + if excludedScreens == nil { + excludedScreens = make(map[string]bool) + } + excludedScreens[nearestScreen.ID] = true + return m.screenNearestRect(rect, isPhysical, excludedScreens) + } + } + return nearestScreen +} + +func (m *ScreenManager) DipToPhysicalPoint(dipPoint Point) Point { + screen := m.ScreenNearestDipPoint(dipPoint) + return screen.dipToPhysicalPoint(dipPoint, false) +} + +func (m *ScreenManager) PhysicalToDipPoint(physicalPoint Point) Point { + screen := m.ScreenNearestPhysicalPoint(physicalPoint) + return screen.physicalToDipPoint(physicalPoint, false) +} + +func (m *ScreenManager) DipToPhysicalRect(dipRect Rect) Rect { + screen := m.ScreenNearestDipRect(dipRect) + return screen.dipToPhysicalRect(dipRect) +} + +func (m *ScreenManager) PhysicalToDipRect(physicalRect Rect) Rect { + screen := m.ScreenNearestPhysicalRect(physicalRect) + return screen.physicalToDipRect(physicalRect) +} + +func (m *ScreenManager) ScreenNearestPhysicalPoint(physicalPoint Point) *Screen { + return m.screenNearestPoint(physicalPoint, true) +} + +func (m *ScreenManager) ScreenNearestDipPoint(dipPoint Point) *Screen { + return m.screenNearestPoint(dipPoint, false) +} + +func (m *ScreenManager) ScreenNearestPhysicalRect(physicalRect Rect) *Screen { + return m.screenNearestRect(physicalRect, true, nil) +} + +func (m *ScreenManager) ScreenNearestDipRect(dipRect Rect) *Screen { + return m.screenNearestRect(dipRect, false, nil) +} + +// ================================================================================================ +// Exported application-level methods for internal convenience and availability to application devs + +func DipToPhysicalPoint(dipPoint Point) Point { + return globalApplication.Screen.DipToPhysicalPoint(dipPoint) +} + +func PhysicalToDipPoint(physicalPoint Point) Point { + return globalApplication.Screen.PhysicalToDipPoint(physicalPoint) +} + +func DipToPhysicalRect(dipRect Rect) Rect { + return globalApplication.Screen.DipToPhysicalRect(dipRect) +} + +func PhysicalToDipRect(physicalRect Rect) Rect { + return globalApplication.Screen.PhysicalToDipRect(physicalRect) +} + +func ScreenNearestPhysicalPoint(physicalPoint Point) *Screen { + return globalApplication.Screen.ScreenNearestPhysicalPoint(physicalPoint) +} + +func ScreenNearestDipPoint(dipPoint Point) *Screen { + return globalApplication.Screen.ScreenNearestDipPoint(dipPoint) +} + +func ScreenNearestPhysicalRect(physicalRect Rect) *Screen { + return globalApplication.Screen.ScreenNearestPhysicalRect(physicalRect) +} + +func ScreenNearestDipRect(dipRect Rect) *Screen { + return globalApplication.Screen.ScreenNearestDipRect(dipRect) +} diff --git a/v3/pkg/application/screenmanager_test.go b/v3/pkg/application/screenmanager_test.go new file mode 100644 index 000000000..1e58e3fd1 --- /dev/null +++ b/v3/pkg/application/screenmanager_test.go @@ -0,0 +1,716 @@ +package application_test + +import ( + "fmt" + "math" + "slices" + "strconv" + "testing" + + "github.com/matryer/is" + "github.com/wailsapp/wails/v3/pkg/application" +) + +type ScreenDef struct { + id int + w, h int + s float32 + parent ScreenDefParent + name string +} + +type ScreenDefParent struct { + id int + align string + offset int +} + +type ScreensLayout struct { + name string + screens []ScreenDef +} + +type ParsedLayout struct { + name string + screens []*application.Screen +} + +func exampleLayouts() []ParsedLayout { + layouts := [][]ScreensLayout{ + { + // Normal examples (demonstrate real life scenarios) + { + name: "Single 4k monitor", + screens: []ScreenDef{ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + }, + }, + { + name: "Two monitors", + screens: []ScreenDef{ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + }, + }, + { + name: "Two monitors (2)", + screens: []ScreenDef{ + {id: 1, w: 1920, h: 1080, s: 1, name: `23" FHD 96DPI`}, + {id: 2, w: 1920, h: 1080, s: 1.25, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI (125%)`}, + }, + }, + { + name: "Three monitors", + screens: []ScreenDef{ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + {id: 3, w: 1920, h: 1080, s: 1.25, parent: ScreenDefParent{id: 1, align: "l", offset: 0}, name: `23" FHD 96DPI (125%)`}, + }, + }, + { + name: "Four monitors", + screens: []ScreenDef{ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + {id: 3, w: 1920, h: 1080, s: 1.25, parent: ScreenDefParent{id: 2, align: "b", offset: 0}, name: `23" FHD 96DPI (125%)`}, + {id: 4, w: 1080, h: 1920, s: 1, parent: ScreenDefParent{id: 1, align: "l", offset: 0}, name: `23" FHD (90deg)`}, + }, + }, + }, + { + // Test cases examples (demonstrate the algorithm basics) + { + name: "Child scaled, Start offset", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1.5, parent: ScreenDefParent{id: 1, align: "r", offset: 600}, name: "Child"}, + }, + }, + { + name: "Child scaled, End offset", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1.5, parent: ScreenDefParent{id: 1, align: "r", offset: -600}, name: "Child"}, + }, + }, + { + name: "Parent scaled, Start offset percent", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 600}, name: "Child"}, + }, + }, + { + name: "Parent scaled, End offset percent", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: -600}, name: "Child"}, + }, + }, + { + name: "Parent scaled, Start align", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1100, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: "Child"}, + }, + }, + { + name: "Parent scaled, End align", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1200, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: "Child"}, + }, + }, + { + name: "Parent scaled, in-between", + screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5, name: "Parent"}, + {id: 2, w: 1200, h: 1500, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: -250}, name: "Child"}, + }, + }, + }, + { + // Edge cases examples + { + name: "Parent order (5 is parent of 4)", + screens: []ScreenDef{ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 600, s: 1.25, parent: ScreenDefParent{id: 1, align: "r", offset: -200}}, + {id: 3, w: 800, h: 800, s: 1.25, parent: ScreenDefParent{id: 2, align: "b", offset: 0}}, + {id: 4, w: 800, h: 1080, s: 1.5, parent: ScreenDefParent{id: 2, align: "re", offset: 100}}, + {id: 5, w: 600, h: 600, s: 1, parent: ScreenDefParent{id: 3, align: "r", offset: 100}}, + }, + }, + { + name: "de-intersection reparent", + screens: []ScreenDef{ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1680, h: 1050, s: 1.25, parent: ScreenDefParent{id: 1, align: "r", offset: 10}}, + {id: 3, w: 1440, h: 900, s: 1.5, parent: ScreenDefParent{id: 1, align: "le", offset: 150}}, + {id: 4, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 3, align: "bc", offset: -200}}, + {id: 5, w: 1024, h: 768, s: 1.25, parent: ScreenDefParent{id: 4, align: "r", offset: 400}}, + }, + }, + { + name: "de-intersection (unattached child)", + screens: []ScreenDef{ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 768, s: 1.5, parent: ScreenDefParent{id: 1, align: "le", offset: 10}}, + {id: 3, w: 1024, h: 768, s: 1.25, parent: ScreenDefParent{id: 2, align: "b", offset: 100}}, + {id: 4, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 3, align: "r", offset: 500}}, + }, + }, + { + name: "Multiple de-intersection", + screens: []ScreenDef{ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 1, align: "be", offset: 0}}, + {id: 3, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 2, align: "b", offset: 300}}, + {id: 4, w: 1024, h: 768, s: 1.5, parent: ScreenDefParent{id: 2, align: "le", offset: 100}}, + {id: 5, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 4, align: "be", offset: 100}}, + }, + }, + { + name: "Multiple de-intersection (left-side)", + screens: []ScreenDef{ + {id: 1, w: 1920, h: 1080, s: 1}, + {id: 2, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 1, align: "le", offset: 0}}, + {id: 3, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 2, align: "b", offset: 300}}, + {id: 4, w: 1024, h: 768, s: 1.5, parent: ScreenDefParent{id: 2, align: "le", offset: 100}}, + {id: 5, w: 1024, h: 768, s: 1, parent: ScreenDefParent{id: 4, align: "be", offset: 100}}, + }, + }, + { + name: "Parent de-intersection child offset", + screens: []ScreenDef{ + {id: 1, w: 1600, h: 1600, s: 1.5}, + {id: 2, w: 800, h: 800, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}}, + {id: 3, w: 800, h: 800, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 800}}, + {id: 4, w: 800, h: 1600, s: 1, parent: ScreenDefParent{id: 2, align: "r", offset: 0}}, + }, + }, + }, + } + + parsedLayouts := []ParsedLayout{} + + for _, section := range layouts { + for _, layout := range section { + parsedLayouts = append(parsedLayouts, parseLayout(layout)) + } + } + + return parsedLayouts +} + +// Parse screens layout from easy-to-define ScreenDef for testing to actual Screens layout +func parseLayout(layout ScreensLayout) ParsedLayout { + screens := []*application.Screen{} + + for _, screen := range layout.screens { + var x, y int + w := screen.w + h := screen.h + + if screen.parent.id > 0 { + idx := slices.IndexFunc(screens, func(s *application.Screen) bool { return s.ID == strconv.Itoa(screen.parent.id) }) + parent := screens[idx].Bounds + offset := screen.parent.offset + align := screen.parent.align + align2 := "" + + if len(align) == 2 { + align2 = string(align[1]) + align = string(align[0]) + } + + x = parent.X + y = parent.Y + // t: top, b: bottom, l: left, r: right, e: edge, c: corner + if align == "t" || align == "b" { + x += offset + if align2 == "e" || align2 == "c" { + x += parent.Width + } + if align2 == "e" { + x -= w + } + if align == "t" { + y -= h + } else { + y += parent.Height + } + } else { + y += offset + if align2 == "e" || align2 == "c" { + y += parent.Height + } + if align2 == "e" { + y -= h + } + if align == "l" { + x -= w + } else { + x += parent.Width + } + } + } + name := screen.name + if name == "" { + name = "Display" + strconv.Itoa(screen.id) + } + screens = append(screens, &application.Screen{ + ID: strconv.Itoa(screen.id), + Name: name, + ScaleFactor: float32(math.Round(float64(screen.s)*100) / 100), + X: x, + Y: y, + Size: application.Size{Width: w, Height: h}, + Bounds: application.Rect{X: x, Y: y, Width: w, Height: h}, + PhysicalBounds: application.Rect{X: x, Y: y, Width: w, Height: h}, + WorkArea: application.Rect{X: x, Y: y, Width: w, Height: h - int(40*screen.s)}, + PhysicalWorkArea: application.Rect{X: x, Y: y, Width: w, Height: h - int(40*screen.s)}, + IsPrimary: screen.id == 1, + Rotation: 0, + }) + } + return ParsedLayout{ + name: layout.name, + screens: screens, + } +} + +func matchRects(r1, r2 application.Rect) error { + threshold := 1.0 + if math.Abs(float64(r1.X-r2.X)) > threshold || + math.Abs(float64(r1.Y-r2.Y)) > threshold || + math.Abs(float64(r1.Width-r2.Width)) > threshold || + math.Abs(float64(r1.Height-r2.Height)) > threshold { + return fmt.Errorf("%v != %v", r1, r2) + } + return nil +} + +// Test screens layout (DPI transformation) +func TestScreenManager_ScreensLayout(t *testing.T) { + sm := application.ScreenManager{} + + t.Run("Child scaled", func(t *testing.T) { + is := is.New(t) + + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1}, + {id: 2, w: 1200, h: 1200, s: 1.5, parent: ScreenDefParent{id: 1, align: "r", offset: 600}}, + }}) + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + screens := sm.GetAll() + is.Equal(len(screens), 2) // 2 screens + is.Equal(screens[0].PhysicalBounds, application.Rect{X: 0, Y: 0, Width: 1200, Height: 1200}) // Parent physical bounds + is.Equal(screens[0].Bounds, screens[0].PhysicalBounds) // Parent no scaling + is.Equal(screens[1].PhysicalBounds, application.Rect{X: 1200, Y: 600, Width: 1200, Height: 1200}) // Child physical bounds + is.Equal(screens[1].Bounds, application.Rect{X: 1200, Y: 600, Width: 800, Height: 800}) // Child DIP bounds + }) + + t.Run("Parent scaled", func(t *testing.T) { + is := is.New(t) + + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5}, + {id: 2, w: 1200, h: 1200, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 600}}, + }}) + + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + screens := sm.GetAll() + is.Equal(len(screens), 2) // 2 screens + is.Equal(screens[0].PhysicalBounds, application.Rect{X: 0, Y: 0, Width: 1200, Height: 1200}) // Parent physical bounds + is.Equal(screens[0].Bounds, application.Rect{X: 0, Y: 0, Width: 800, Height: 800}) // Parent DIP bounds + is.Equal(screens[1].PhysicalBounds, application.Rect{X: 1200, Y: 600, Width: 1200, Height: 1200}) // Child physical bounds + is.Equal(screens[1].Bounds, application.Rect{X: 800, Y: 400, Width: 1200, Height: 1200}) // Child DIP bounds + }) +} + +// Test basic transformation between physical and DIP coordinates +func TestScreenManager_BasicTranformation(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1}, + {id: 2, w: 1200, h: 1200, s: 1.5, parent: ScreenDefParent{id: 1, align: "r", offset: 600}}, + }}) + + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + pt := application.Point{X: 100, Y: 100} + is.Equal(sm.DipToPhysicalPoint(pt), pt) // DipToPhysicalPoint screen1 + is.Equal(sm.PhysicalToDipPoint(pt), pt) // PhysicalToDipPoint screen1 + + ptDip := application.Point{X: 1300, Y: 700} + ptPhysical := application.Point{X: 1350, Y: 750} + is.Equal(sm.DipToPhysicalPoint(ptDip), ptPhysical) // DipToPhysicalPoint screen2 + is.Equal(sm.PhysicalToDipPoint(ptPhysical), ptDip) // PhysicalToDipPoint screen2 + + rect := application.Rect{X: 100, Y: 100, Width: 200, Height: 300} + is.Equal(sm.DipToPhysicalRect(rect), rect) // DipToPhysicalRect screen1 + is.Equal(sm.PhysicalToDipRect(rect), rect) // DipToPhysicalRect screen1 + + rectDip := application.Rect{X: 1300, Y: 700, Width: 200, Height: 300} + rectPhysical := application.Rect{X: 1350, Y: 750, Width: 300, Height: 450} + is.Equal(sm.DipToPhysicalRect(rectDip), rectPhysical) // DipToPhysicalRect screen2 + is.Equal(sm.PhysicalToDipRect(rectPhysical), rectDip) // DipToPhysicalRect screen2 + + rectDip = application.Rect{X: 2200, Y: 250, Width: 200, Height: 300} + rectPhysical = application.Rect{X: 2700, Y: 75, Width: 300, Height: 450} + is.Equal(sm.DipToPhysicalRect(rectDip), rectPhysical) // DipToPhysicalRect outside screen2 + is.Equal(sm.PhysicalToDipRect(rectPhysical), rectDip) // DipToPhysicalRect outside screen2 +} + +func TestScreenManager_PrimaryScreen(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + + for _, layout := range exampleLayouts() { + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + is.Equal(sm.GetPrimary(), layout.screens[0]) // Primary screen + } + + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5}, + {id: 2, w: 1200, h: 1200, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 600}}, + }}) + + layout.screens[0], layout.screens[1] = layout.screens[1], layout.screens[0] + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + is.Equal(sm.GetPrimary(), layout.screens[1]) // Primary screen + + layout.screens[1].IsPrimary = false + err = sm.LayoutScreens(layout.screens) + is.True(err != nil) // Should error when no primary screen found +} + +// Test edge alignment between transformation +// (points and rects on the screen edge should transform to the same precise edge position) +func TestScreenManager_EdgeAlign(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + + for _, layout := range exampleLayouts() { + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + for _, screen := range sm.GetAll() { + ptOriginDip := screen.Bounds.Origin() + ptOriginPhysical := screen.PhysicalBounds.Origin() + ptCornerDip := screen.Bounds.InsideCorner() + ptCornerPhysical := screen.PhysicalBounds.InsideCorner() + + is.Equal(sm.DipToPhysicalPoint(ptOriginDip), ptOriginPhysical) // DipToPhysicalPoint Origin + is.Equal(sm.PhysicalToDipPoint(ptOriginPhysical), ptOriginDip) // PhysicalToDipPoint Origin + is.Equal(sm.DipToPhysicalPoint(ptCornerDip), ptCornerPhysical) // DipToPhysicalPoint Corner + is.Equal(sm.PhysicalToDipPoint(ptCornerPhysical), ptCornerDip) // PhysicalToDipPoint Corner + + rectOriginDip := application.Rect{X: ptOriginDip.X, Y: ptOriginDip.Y, Width: 100, Height: 100} + rectOriginPhysical := application.Rect{X: ptOriginPhysical.X, Y: ptOriginPhysical.Y, Width: 100, Height: 100} + rectCornerDip := application.Rect{X: ptCornerDip.X - 99, Y: ptCornerDip.Y - 99, Width: 100, Height: 100} + rectCornerPhysical := application.Rect{X: ptCornerPhysical.X - 99, Y: ptCornerPhysical.Y - 99, Width: 100, Height: 100} + + is.Equal(sm.DipToPhysicalRect(rectOriginDip).Origin(), rectOriginPhysical.Origin()) // DipToPhysicalRect Origin + is.Equal(sm.PhysicalToDipRect(rectOriginPhysical).Origin(), rectOriginDip.Origin()) // PhysicalToDipRect Origin + is.Equal(sm.DipToPhysicalRect(rectCornerDip).Corner(), rectCornerPhysical.Corner()) // DipToPhysicalRect Corner + is.Equal(sm.PhysicalToDipRect(rectCornerPhysical).Corner(), rectCornerDip.Corner()) // PhysicalToDipRect Corner + } + } +} + +func TestScreenManager_ProbePoints(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + threshold := 1.0 + steps := 3 + + for _, layout := range exampleLayouts() { + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + for _, screen := range sm.GetAll() { + for i := 0; i <= 1; i++ { + isDip := (i == 0) + + var b application.Rect + if isDip { + b = screen.Bounds + } else { + b = screen.PhysicalBounds + } + + xStep := b.Width / steps + yStep := b.Height / steps + if xStep < 1 { + xStep = 1 + } + if yStep < 1 { + yStep = 1 + } + pt := b.Origin() + xDone := false + yDone := false + + for !yDone { + if pt.Y > b.InsideCorner().Y { + pt.Y = b.InsideCorner().Y + yDone = true + } + + pt.X = b.X + xDone = false + + for !xDone { + if pt.X > b.InsideCorner().X { + pt.X = b.InsideCorner().X + xDone = true + } + var ptDblTransformed application.Point + + if isDip { + ptDblTransformed = sm.PhysicalToDipPoint(sm.DipToPhysicalPoint(pt)) + } else { + ptDblTransformed = sm.DipToPhysicalPoint(sm.PhysicalToDipPoint(pt)) + } + + is.True(math.Abs(float64(ptDblTransformed.X-pt.X)) <= threshold) + is.True(math.Abs(float64(ptDblTransformed.Y-pt.Y)) <= threshold) + pt.X += xStep + } + pt.Y += yStep + } + } + } + } +} + +// Test transformation drift over time +func TestScreenManager_TransformationDrift(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + + for _, layout := range exampleLayouts() { + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + for _, screen := range sm.GetAll() { + rectPhysicalOriginal := application.Rect{ + X: screen.PhysicalBounds.X + 100, + Y: screen.PhysicalBounds.Y + 100, + Width: 123, + Height: 123, + } + + // Slide the position to catch any rounding errors + for i := 0; i < 10; i++ { + rectPhysicalOriginal.X++ + rectPhysicalOriginal.Y++ + rectPhysical := rectPhysicalOriginal + // Transform back and forth several times to make sure no drift is introduced over time + for j := 0; j < 10; j++ { + rectDip := sm.PhysicalToDipRect(rectPhysical) + rectPhysical = sm.DipToPhysicalRect(rectDip) + } + is.NoErr(matchRects(rectPhysical, rectPhysicalOriginal)) + } + } + } +} + +func TestScreenManager_ScreenNearestRect(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 3840, h: 2160, s: 163.0 / 96, name: `27" 4K UHD 163DPI`}, + {id: 2, w: 1920, h: 1080, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}, name: `23" FHD 96DPI`}, + {id: 3, w: 1920, h: 1080, s: 1.25, parent: ScreenDefParent{id: 1, align: "l", offset: 0}, name: `23" FHD 96DPI (125%)`}, + }}) + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + type Rects map[string][]application.Rect + + t.Run("DIP rects", func(t *testing.T) { + is := is.New(t) + rects := Rects{ + "1": []application.Rect{ + {X: -150, Y: 260, Width: 400, Height: 300}, + {X: -250, Y: 750, Width: 400, Height: 300}, + {X: -450, Y: 950, Width: 400, Height: 300}, + {X: 800, Y: 1350, Width: 400, Height: 300}, + {X: 2000, Y: 100, Width: 400, Height: 300}, + {X: 2100, Y: 950, Width: 400, Height: 300}, + {X: 2350, Y: 1200, Width: 400, Height: 300}, + }, + "2": []application.Rect{ + {X: 2100, Y: 50, Width: 400, Height: 300}, + {X: 2150, Y: 950, Width: 400, Height: 300}, + {X: 2450, Y: 1150, Width: 400, Height: 300}, + {X: 4300, Y: 400, Width: 400, Height: 300}, + }, + "3": []application.Rect{ + {X: -2000, Y: 100, Width: 400, Height: 300}, + {X: -220, Y: 200, Width: 400, Height: 300}, + {X: -300, Y: 750, Width: 400, Height: 300}, + {X: -500, Y: 900, Width: 400, Height: 300}, + }, + } + + for screenID, screenRects := range rects { + for _, rect := range screenRects { + screen := sm.ScreenNearestDipRect(rect) + is.Equal(screen.ID, screenID) + } + } + }) + t.Run("Physical rects", func(t *testing.T) { + is := is.New(t) + rects := Rects{ + "1": []application.Rect{ + {X: -150, Y: 100, Width: 400, Height: 300}, + {X: -250, Y: 1500, Width: 400, Height: 300}, + {X: 3600, Y: 100, Width: 400, Height: 300}, + }, + "2": []application.Rect{ + {X: 3700, Y: 100, Width: 400, Height: 300}, + {X: 4000, Y: 1150, Width: 400, Height: 300}, + }, + "3": []application.Rect{ + {X: -250, Y: 100, Width: 400, Height: 300}, + {X: -300, Y: 950, Width: 400, Height: 300}, + {X: -1000, Y: 1000, Width: 400, Height: 300}, + }, + } + + for screenID, screenRects := range rects { + for _, rect := range screenRects { + screen := sm.ScreenNearestPhysicalRect(rect) + is.Equal(screen.ID, screenID) + } + } + }) + + // DIP rect is near screen1 but when transformed becomes near screen2. + // To have a consistent transformation back & forth, screen nearest physical rect + // should be the one given by ScreenNearestDipRect + t.Run("Edge case 1", func(t *testing.T) { + is := is.New(t) + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1}, + {id: 2, w: 1200, h: 1300, s: 1.5, parent: ScreenDefParent{id: 1, align: "r", offset: -20}}, + }}) + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + rectDip := application.Rect{X: 1020, Y: 800, Width: 400, Height: 300} + rectPhysical := sm.DipToPhysicalRect(rectDip) + + screenDip := sm.ScreenNearestDipRect(rectDip) + screenPhysical := sm.ScreenNearestPhysicalRect(rectPhysical) + is.Equal(screenDip.ID, "2") // screenDip + is.Equal(screenPhysical.ID, "2") // screenPhysical + + rectDblTransformed := sm.PhysicalToDipRect(rectPhysical) + is.NoErr(matchRects(rectDblTransformed, rectDip)) // double transformation + }) +} + +// Unsolved edge cases +func TestScreenManager_UnsolvedEdgeCases(t *testing.T) { + sm := application.ScreenManager{} + is := is.New(t) + + // Edge case 1: invalid DIP rect location + // there could be a setup where some dip rects locations are invalid, meaning that there's no + // physical rect that could produce that dip rect at this location + // Not sure how to solve this scenario + t.Run("Edge case 1: invalid dip rect", func(t *testing.T) { + t.Skip("Unsolved edge case") + is := is.New(t) + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1}, + {id: 2, w: 1200, h: 1100, s: 1.5, parent: ScreenDefParent{id: 1, align: "r", offset: 0}}, + }}) + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + rectDip := application.Rect{X: 1050, Y: 700, Width: 400, Height: 300} + rectPhysical := sm.DipToPhysicalRect(rectDip) + + screenDip := sm.ScreenNearestDipRect(rectDip) + screenPhysical := sm.ScreenNearestPhysicalRect(rectPhysical) + is.Equal(screenDip.ID, screenPhysical.ID) + + rectDblTransformed := sm.PhysicalToDipRect(rectPhysical) + is.NoErr(matchRects(rectDblTransformed, rectDip)) // double transformation + }) + + // Edge case 2: physical rect that changes when double transformed + // there could be a setup where a dip rect at some locations could be produced by two different physical rects + // causing one of these physical rects to be changed to the other when double transformed + // Not sure how to solve this scenario + t.Run("Edge case 2: changed physical rect", func(t *testing.T) { + t.Skip("Unsolved edge case") + is := is.New(t) + layout := parseLayout(ScreensLayout{screens: []ScreenDef{ + {id: 1, w: 1200, h: 1200, s: 1.5}, + {id: 2, w: 1200, h: 900, s: 1, parent: ScreenDefParent{id: 1, align: "r", offset: 0}}, + }}) + err := sm.LayoutScreens(layout.screens) + is.NoErr(err) + + rectPhysical := application.Rect{X: 1050, Y: 890, Width: 400, Height: 300} + rectDblTransformed := sm.DipToPhysicalRect(sm.PhysicalToDipRect(rectPhysical)) + is.NoErr(matchRects(rectDblTransformed, rectPhysical)) // double transformation + }) +} + +func BenchmarkScreenManager_LayoutScreens(b *testing.B) { + sm := application.ScreenManager{} + layouts := exampleLayouts() + screens := layouts[3].screens + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + sm.LayoutScreens(screens) + } +} + +func BenchmarkScreenManager_TransformPoint(b *testing.B) { + sm := application.ScreenManager{} + layouts := exampleLayouts() + screens := layouts[3].screens + sm.LayoutScreens(screens) + + pt := application.Point{X: 500, Y: 500} + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + sm.DipToPhysicalPoint(pt) + } +} + +func BenchmarkScreenManager_TransformRect(b *testing.B) { + sm := application.ScreenManager{} + layouts := exampleLayouts() + screens := layouts[3].screens + sm.LayoutScreens(screens) + + rect := application.Rect{X: 500, Y: 500, Width: 800, Height: 600} + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + sm.DipToPhysicalRect(rect) + } +} diff --git a/v3/pkg/application/services.go b/v3/pkg/application/services.go new file mode 100644 index 000000000..582d135b0 --- /dev/null +++ b/v3/pkg/application/services.go @@ -0,0 +1,134 @@ +package application + +import ( + "context" + "reflect" +) + +// Service wraps a bound type instance. +// The zero value of Service is invalid. +// Valid values may only be obtained by calling [NewService]. +type Service struct { + instance any + options ServiceOptions +} + +// ServiceOptions provides optional parameters for calls to [NewService]. +type ServiceOptions struct { + // Name can be set to override the name of the service + // for logging and debugging purposes. + // + // If empty, it will default + // either to the value obtained through the [ServiceName] interface, + // or to the type name. + Name string + + // If the service instance implements [http.Handler], + // it will be mounted on the internal asset server + // at the prefix specified by Route. + Route string + + // MarshalError will be called if non-nil + // to marshal to JSON the error values returned by this service's methods. + // + // MarshalError is not allowed to fail, + // but it may return a nil slice to fall back + // to the globally configured error handler. + // + // If the returned slice is not nil, it must contain valid JSON. + MarshalError func(error) []byte +} + +// DefaultServiceOptions specifies the default values of service options, +// used when no [ServiceOptions] instance is provided to [NewService]. +var DefaultServiceOptions = ServiceOptions{} + +// NewService returns a Service value wrapping the given pointer. +// If T is not a concrete named type, the returned value is invalid. +func NewService[T any](instance *T) Service { + return Service{instance, DefaultServiceOptions} +} + +// NewServiceWithOptions returns a Service value wrapping the given pointer +// and specifying the given service options. +// If T is not a concrete named type, the returned value is invalid. +func NewServiceWithOptions[T any](instance *T, options ServiceOptions) Service { + service := NewService(instance) // Delegate to NewService so that the static analyser may detect T. Do not remove this call. + service.options = options + return service +} + +// Instance returns the service instance provided to [NewService]. +func (s Service) Instance() any { + return s.instance +} + +// ServiceName returns the name of the service +// +// This is an *optional* method that may be implemented by service instances. +// It is used for logging and debugging purposes. +// +// If a non-empty name is provided with [ServiceOptions], +// it takes precedence over the one returned by the ServiceName method. +type ServiceName interface { + ServiceName() string +} + +// ServiceStartup is an *optional* method that may be implemented by service instances. +// +// This method will be called during application startup and will receive a copy of the options +// specified at creation time. It can be used for initialising resources. +// +// The context will be valid as long as the application is running, +// and will be canceled right before shutdown. +// +// Services are guaranteed to receive the startup notification +// in the exact order in which they were either +// listed in [Options.Services] or registered with [App.RegisterService], +// with those from [Options.Services] coming first. +// +// If the return value is non-nil, the startup process aborts +// and [App.Run] returns the error wrapped with [fmt.Errorf] +// in a user-friendly message comprising the service name. +// The original error can be retrieved either by calling the Unwrap method +// or through the [errors.As] API. +// +// When that happens, service instances that have been already initialised +// receive a shutdown notification. +type ServiceStartup interface { + ServiceStartup(ctx context.Context, options ServiceOptions) error +} + +// ServiceShutdown is an *optional* method that may be implemented by service instances. +// +// This method will be called during application shutdown. It can be used for cleaning up resources. +// If a service has received a startup notification, +// then it is guaranteed to receive a shutdown notification too, +// except in case of unhandled panics during shutdown. +// +// Services receive shutdown notifications in reverse registration order, +// after all user-provided shutdown hooks have run (see [App.OnShutdown]). +// +// If the return value is non-nil, it is passed to the application's +// configured error handler at [Options.ErrorHandler], +// wrapped with [fmt.Errorf] in a user-friendly message comprising the service name. +// The default behaviour is to log the error along with the service name. +// The original error can be retrieved either by calling the Unwrap method +// or through the [errors.As] API. +type ServiceShutdown interface { + ServiceShutdown() error +} + +func getServiceName(service Service) string { + if service.options.Name != "" { + return service.options.Name + } + + // Check if the service implements the ServiceName interface + if s, ok := service.Instance().(ServiceName); ok { + return s.ServiceName() + } + + // Finally, get the name from the type. + return reflect.TypeOf(service.Instance()).Elem().String() +} diff --git a/v3/pkg/application/single_instance.go b/v3/pkg/application/single_instance.go new file mode 100644 index 000000000..24bbf5c31 --- /dev/null +++ b/v3/pkg/application/single_instance.go @@ -0,0 +1,215 @@ +package application + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "encoding/json" + "errors" + "os" + "path/filepath" + "sync" +) + +var alreadyRunningError = errors.New("application is already running") +var secondInstanceBuffer = make(chan string, 1) +var once sync.Once + +// SecondInstanceData contains information about the second instance launch +type SecondInstanceData struct { + Args []string `json:"args"` + WorkingDir string `json:"workingDir"` + AdditionalData map[string]string `json:"additionalData,omitempty"` +} + +// SingleInstanceOptions defines options for single instance functionality +type SingleInstanceOptions struct { + // UniqueID is used to identify the application instance + // This should be unique per application, e.g. "com.myapp.myapplication" + UniqueID string + + // OnSecondInstanceLaunch is called when a second instance of the application is launched + // The callback receives data about the second instance launch + OnSecondInstanceLaunch func(data SecondInstanceData) + + // AdditionalData allows passing custom data from second instance to first + AdditionalData map[string]string + + // ExitCode is the exit code to use when the second instance exits + ExitCode int + + // EncryptionKey is a 32-byte key used for encrypting instance communication + // If not provided (zero array), data will be sent unencrypted + EncryptionKey [32]byte +} + +// platformLock is the interface that platform-specific lock implementations must implement +type platformLock interface { + // acquire attempts to acquire the lock + acquire(uniqueID string) error + // release releases the lock and cleans up resources + release() + // notify sends data to the first instance + notify(data string) error +} + +// singleInstanceManager handles the single instance functionality +type singleInstanceManager struct { + options *SingleInstanceOptions + lock platformLock + app *App +} + +func newSingleInstanceManager(app *App, options *SingleInstanceOptions) (*singleInstanceManager, error) { + if options == nil { + return nil, nil + } + + manager := &singleInstanceManager{ + options: options, + app: app, + } + + // Launch second instance data listener + once.Do(func() { + go func() { + defer handlePanic() + for encryptedData := range secondInstanceBuffer { + var secondInstanceData SecondInstanceData + var jsonData []byte + var err error + + // Check if encryption key is non-zero + var zeroKey [32]byte + if options.EncryptionKey != zeroKey { + // Try to decrypt the data + jsonData, err = decrypt(options.EncryptionKey, encryptedData) + if err != nil { + continue // Skip invalid data + } + } else { + jsonData = []byte(encryptedData) + } + + if err := json.Unmarshal(jsonData, &secondInstanceData); err == nil && manager.options.OnSecondInstanceLaunch != nil { + manager.options.OnSecondInstanceLaunch(secondInstanceData) + } + } + }() + }) + + // Create platform-specific lock + lock, err := newPlatformLock(manager) + if err != nil { + return nil, err + } + + manager.lock = lock + + // Try to acquire the lock + err = lock.acquire(options.UniqueID) + if err != nil { + return manager, err + } + + return manager, nil +} + +func (m *singleInstanceManager) cleanup() { + if m == nil || m.lock == nil { + return + } + m.lock.release() +} + +// encrypt encrypts data using AES-256-GCM +func encrypt(key [32]byte, plaintext []byte) (string, error) { + block, err := aes.NewCipher(key[:]) + if err != nil { + return "", err + } + + nonce := make([]byte, 12) + if _, err := rand.Read(nonce); err != nil { + return "", err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) + encrypted := append(nonce, ciphertext...) + return base64.StdEncoding.EncodeToString(encrypted), nil +} + +// decrypt decrypts data using AES-256-GCM +func decrypt(key [32]byte, encrypted string) ([]byte, error) { + data, err := base64.StdEncoding.DecodeString(encrypted) + if err != nil { + return nil, err + } + + if len(data) < 12 { + return nil, errors.New("invalid encrypted data") + } + + block, err := aes.NewCipher(key[:]) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonce := data[:12] + ciphertext := data[12:] + + return aesgcm.Open(nil, nonce, ciphertext, nil) +} + +// notifyFirstInstance sends data to the first instance of the application +func (m *singleInstanceManager) notifyFirstInstance() error { + data := SecondInstanceData{ + Args: os.Args, + WorkingDir: getCurrentWorkingDir(), + AdditionalData: m.options.AdditionalData, + } + + serialized, err := json.Marshal(data) + if err != nil { + return err + } + + // Check if encryption key is non-zero + var zeroKey [32]byte + if m.options.EncryptionKey != zeroKey { + encrypted, err := encrypt(m.options.EncryptionKey, serialized) + if err != nil { + return err + } + return m.lock.notify(encrypted) + } + + return m.lock.notify(string(serialized)) +} + +func getCurrentWorkingDir() string { + dir, err := os.Getwd() + if err != nil { + return "" + } + return dir +} + +// getLockPath returns the path to the lock file for Unix systems +func getLockPath(uniqueID string) string { + // Use system temp directory + tmpDir := os.TempDir() + lockFileName := uniqueID + ".lock" + return filepath.Join(tmpDir, lockFileName) +} diff --git a/v3/pkg/application/single_instance_darwin.go b/v3/pkg/application/single_instance_darwin.go new file mode 100644 index 000000000..4101294d8 --- /dev/null +++ b/v3/pkg/application/single_instance_darwin.go @@ -0,0 +1,96 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa + +#include +#import +#import + +static void SendDataToFirstInstance(char *singleInstanceUniqueId, char* message) { + [[NSDistributedNotificationCenter defaultCenter] + postNotificationName:[NSString stringWithUTF8String:singleInstanceUniqueId] + object:nil + userInfo:@{@"message": [NSString stringWithUTF8String:message]} + deliverImmediately:YES]; +} + +*/ +import "C" +import ( + "os" + "syscall" + "unsafe" +) + +type darwinLock struct { + file *os.File + uniqueID string + manager *singleInstanceManager +} + +func newPlatformLock(manager *singleInstanceManager) (platformLock, error) { + return &darwinLock{ + manager: manager, + }, nil +} + +func (l *darwinLock) acquire(uniqueID string) error { + l.uniqueID = uniqueID + lockFilePath := os.TempDir() + lockFileName := uniqueID + ".lock" + var err error + l.file, err = createLockFile(lockFilePath + "/" + lockFileName) + if err != nil { + return alreadyRunningError + } + return nil +} + +func (l *darwinLock) release() { + if l.file != nil { + syscall.Flock(int(l.file.Fd()), syscall.LOCK_UN) + l.file.Close() + os.Remove(l.file.Name()) + l.file = nil + } +} + +func (l *darwinLock) notify(data string) error { + singleInstanceUniqueId := C.CString(l.uniqueID) + defer C.free(unsafe.Pointer(singleInstanceUniqueId)) + cData := C.CString(data) + defer C.free(unsafe.Pointer(cData)) + + C.SendDataToFirstInstance(singleInstanceUniqueId, cData) + + os.Exit(l.manager.options.ExitCode) + return nil +} + +// CreateLockFile tries to create a file with given name and acquire an +// exclusive lock on it. If the file already exists AND is still locked, it will +// fail. +func createLockFile(filename string) (*os.File, error) { + file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + return nil, err + } + + err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) + if err != nil { + file.Close() + return nil, err + } + + return file, nil +} + +//export handleSecondInstanceData +func handleSecondInstanceData(secondInstanceMessage *C.char) { + message := C.GoString(secondInstanceMessage) + secondInstanceBuffer <- message +} diff --git a/v3/pkg/application/single_instance_linux.go b/v3/pkg/application/single_instance_linux.go new file mode 100644 index 000000000..28c9e5483 --- /dev/null +++ b/v3/pkg/application/single_instance_linux.go @@ -0,0 +1,101 @@ +//go:build linux + +package application + +import ( + "errors" + "os" + "strings" + "sync" + "syscall" + + "github.com/godbus/dbus/v5" +) + +type dbusHandler func(string) + +var setup sync.Once + +func (f dbusHandler) SendMessage(message string) *dbus.Error { + f(message) + return nil +} + +type linuxLock struct { + file *os.File + uniqueID string + dbusPath string + dbusName string + manager *singleInstanceManager +} + +func newPlatformLock(manager *singleInstanceManager) (platformLock, error) { + return &linuxLock{ + manager: manager, + }, nil +} + +func (l *linuxLock) acquire(uniqueID string) error { + if uniqueID == "" { + return errors.New("UniqueID is required for single instance lock") + } + + id := "wails_app_" + strings.ReplaceAll(strings.ReplaceAll(uniqueID, "-", "_"), ".", "_") + + l.dbusName = "org." + id + ".SingleInstance" + l.dbusPath = "/org/" + id + "/SingleInstance" + + conn, err := dbus.ConnectSessionBus() + // if we will reach any error during establishing connection or sending message we will just continue. + // It should not be the case that such thing will happen actually, but just in case. + if err != nil { + return err + } + + setup.Do(func() { + f := dbusHandler(func(message string) { + secondInstanceBuffer <- message + }) + + err = conn.Export(f, dbus.ObjectPath(l.dbusPath), l.dbusName) + }) + if err != nil { + return err + } + + reply, err := conn.RequestName(l.dbusName, dbus.NameFlagDoNotQueue) + if err != nil { + return err + } + + // if name already taken, try to send args to existing instance, if no success just launch new instance + if reply == dbus.RequestNameReplyExists { + return alreadyRunningError + } + return nil +} + +func (l *linuxLock) release() { + if l.file != nil { + syscall.Flock(int(l.file.Fd()), syscall.LOCK_UN) + l.file.Close() + os.Remove(l.file.Name()) + l.file = nil + } +} + +func (l *linuxLock) notify(data string) error { + conn, err := dbus.ConnectSessionBus() + // if we will reach any error during establishing connection or sending message we will just continue. + // It should not be the case that such thing will happen actually, but just in case. + if err != nil { + return err + } + + err = conn.Object(l.dbusName, dbus.ObjectPath(l.dbusPath)).Call(l.dbusName+".SendMessage", 0, data).Store() + if err != nil { + return err + } + os.Exit(l.manager.options.ExitCode) + return nil +} diff --git a/v3/pkg/application/single_instance_windows.go b/v3/pkg/application/single_instance_windows.go new file mode 100644 index 000000000..b92b2749a --- /dev/null +++ b/v3/pkg/application/single_instance_windows.go @@ -0,0 +1,129 @@ +//go:build windows + +package application + +import ( + "errors" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v3/pkg/w32" + "golang.org/x/sys/windows" +) + +var ( + user32 = syscall.NewLazyDLL("user32.dll") +) + +type windowsLock struct { + handle syscall.Handle + uniqueID string + msgString string + hwnd w32.HWND + manager *singleInstanceManager + className string + windowName string +} + +func newPlatformLock(manager *singleInstanceManager) (platformLock, error) { + return &windowsLock{ + manager: manager, + }, nil +} + +func (l *windowsLock) acquire(uniqueID string) error { + if uniqueID == "" { + return errors.New("UniqueID is required for single instance lock") + } + + l.uniqueID = uniqueID + id := "wails-app-" + uniqueID + l.className = id + "-sic" + l.windowName = id + "-siw" + mutexName := id + "-sim" + + _, err := windows.CreateMutex(nil, false, windows.StringToUTF16Ptr(mutexName)) + if err != nil { + // Find the window + return alreadyRunningError + } else { + l.hwnd = createEventTargetWindow(l.className, l.windowName) + } + + return nil +} + +func (l *windowsLock) release() { + if l.handle != 0 { + syscall.CloseHandle(l.handle) + l.handle = 0 + } + if l.hwnd != 0 { + w32.DestroyWindow(l.hwnd) + l.hwnd = 0 + } +} + +func (l *windowsLock) notify(data string) error { + + // app is already running + hwnd := w32.FindWindowW(windows.StringToUTF16Ptr(l.className), windows.StringToUTF16Ptr(l.windowName)) + + if hwnd == 0 { + return errors.New("unable to notify other instance") + } + + w32.SendMessageToWindow(hwnd, data) + + return nil +} + +func createEventTargetWindow(className string, windowName string) w32.HWND { + var class w32.WNDCLASSEX + class.Size = uint32(unsafe.Sizeof(class)) + class.Style = 0 + class.WndProc = syscall.NewCallback(wndProc) + class.ClsExtra = 0 + class.WndExtra = 0 + class.Instance = w32.GetModuleHandle("") + class.Icon = 0 + class.Cursor = 0 + class.Background = 0 + class.MenuName = nil + class.ClassName = w32.MustStringToUTF16Ptr(className) + class.IconSm = 0 + + w32.RegisterClassEx(&class) + + // Create hidden message-only window + hwnd := w32.CreateWindowEx( + 0, + w32.MustStringToUTF16Ptr(className), + w32.MustStringToUTF16Ptr(windowName), + 0, + 0, + 0, + 0, + 0, + w32.HWND_MESSAGE, + 0, + w32.GetModuleHandle(""), + nil, + ) + + return hwnd +} + +func wndProc(hwnd w32.HWND, msg uint32, wparam w32.WPARAM, lparam w32.LPARAM) w32.LRESULT { + if msg == w32.WM_COPYDATA { + ldata := (*w32.COPYDATASTRUCT)(unsafe.Pointer(lparam)) + + if ldata.DwData == w32.WMCOPYDATA_SINGLE_INSTANCE_DATA { + serialized := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ldata.LpData))) + secondInstanceBuffer <- serialized + } + return w32.LRESULT(0) + } + + return w32.DefWindowProc(hwnd, msg, wparam, lparam) +} diff --git a/v3/pkg/application/system_tray_manager.go b/v3/pkg/application/system_tray_manager.go new file mode 100644 index 000000000..16bdf5153 --- /dev/null +++ b/v3/pkg/application/system_tray_manager.go @@ -0,0 +1,44 @@ +package application + +// SystemTrayManager manages system tray-related operations +type SystemTrayManager struct { + app *App +} + +// newSystemTrayManager creates a new SystemTrayManager instance +func newSystemTrayManager(app *App) *SystemTrayManager { + return &SystemTrayManager{ + app: app, + } +} + +// New creates a new system tray +func (stm *SystemTrayManager) New() *SystemTray { + id := stm.getNextID() + newSystemTray := newSystemTray(id) + + stm.app.systemTraysLock.Lock() + stm.app.systemTrays[id] = newSystemTray + stm.app.systemTraysLock.Unlock() + + stm.app.runOrDeferToAppRun(newSystemTray) + + return newSystemTray +} + +// getNextID generates the next system tray ID (internal use) +func (stm *SystemTrayManager) getNextID() uint { + stm.app.systemTrayIDLock.Lock() + defer stm.app.systemTrayIDLock.Unlock() + stm.app.systemTrayID++ + return stm.app.systemTrayID +} + +// destroy destroys a system tray (internal use) +func (stm *SystemTrayManager) destroy(tray *SystemTray) { + // Remove the system tray from the app.systemTrays map + stm.app.systemTraysLock.Lock() + delete(stm.app.systemTrays, tray.id) + stm.app.systemTraysLock.Unlock() + tray.destroy() +} diff --git a/v3/pkg/application/systemtray.go b/v3/pkg/application/systemtray.go new file mode 100644 index 000000000..ef0525e54 --- /dev/null +++ b/v3/pkg/application/systemtray.go @@ -0,0 +1,333 @@ +package application + +import ( + "errors" + "runtime" + "sync" + "time" + + "github.com/wailsapp/wails/v3/pkg/events" +) + +type IconPosition int + +const ( + NSImageNone = iota + NSImageOnly + NSImageLeft + NSImageRight + NSImageBelow + NSImageAbove + NSImageOverlaps + NSImageLeading + NSImageTrailing +) + +type systemTrayImpl interface { + setLabel(label string) + setTooltip(tooltip string) + run() + setIcon(icon []byte) + setMenu(menu *Menu) + setIconPosition(position IconPosition) + setTemplateIcon(icon []byte) + destroy() + setDarkModeIcon(icon []byte) + bounds() (*Rect, error) + getScreen() (*Screen, error) + positionWindow(window Window, offset int) error + openMenu() + Show() + Hide() +} + +type SystemTray struct { + id uint + label string + tooltip string + icon []byte + darkModeIcon []byte + iconPosition IconPosition + + clickHandler func() + rightClickHandler func() + doubleClickHandler func() + rightDoubleClickHandler func() + mouseEnterHandler func() + mouseLeaveHandler func() + onMenuOpen func() + onMenuClose func() + + // Platform specific implementation + impl systemTrayImpl + menu *Menu + isTemplateIcon bool + attachedWindow WindowAttachConfig +} + +func newSystemTray(id uint) *SystemTray { + result := &SystemTray{ + id: id, + label: "", + tooltip: "", + iconPosition: NSImageLeading, + attachedWindow: WindowAttachConfig{ + Window: nil, + Offset: 0, + Debounce: 200 * time.Millisecond, + }, + } + result.clickHandler = result.defaultClickHandler + return result +} + +func (s *SystemTray) SetLabel(label string) { + if s.impl == nil { + s.label = label + return + } + InvokeSync(func() { + s.impl.setLabel(label) + }) +} + +func (s *SystemTray) Label() string { + return s.label +} + +func (s *SystemTray) Run() { + s.impl = newSystemTrayImpl(s) + + if s.attachedWindow.Window != nil { + // Setup listener + s.attachedWindow.Window.OnWindowEvent(events.Common.WindowLostFocus, func(event *WindowEvent) { + s.attachedWindow.Window.Hide() + // Special handler for Windows + if runtime.GOOS == "windows" { + // We don't do this unless the window has already been shown + if s.attachedWindow.hasBeenShown == false { + return + } + s.attachedWindow.justClosed = true + go func() { + defer handlePanic() + time.Sleep(s.attachedWindow.Debounce) + s.attachedWindow.justClosed = false + }() + } + }) + } + + InvokeSync(s.impl.run) +} + +func (s *SystemTray) PositionWindow(window Window, offset int) error { + if s.impl == nil { + return errors.New("system tray not running") + } + return InvokeSyncWithError(func() error { + return s.impl.positionWindow(window, offset) + }) +} + +func (s *SystemTray) SetIcon(icon []byte) *SystemTray { + if s.impl == nil { + s.icon = icon + } else { + InvokeSync(func() { + s.impl.setIcon(icon) + }) + } + return s +} + +func (s *SystemTray) SetDarkModeIcon(icon []byte) *SystemTray { + if s.impl == nil { + s.darkModeIcon = icon + } else { + InvokeSync(func() { + s.impl.setDarkModeIcon(icon) + }) + } + return s +} + +func (s *SystemTray) SetMenu(menu *Menu) *SystemTray { + if s.impl == nil { + s.menu = menu + } else { + InvokeSync(func() { + s.impl.setMenu(menu) + }) + } + return s +} + +func (s *SystemTray) SetIconPosition(iconPosition IconPosition) *SystemTray { + if s.impl == nil { + s.iconPosition = iconPosition + } else { + InvokeSync(func() { + s.impl.setIconPosition(iconPosition) + }) + } + return s +} + +func (s *SystemTray) SetTemplateIcon(icon []byte) *SystemTray { + if s.impl == nil { + s.icon = icon + s.isTemplateIcon = true + } else { + InvokeSync(func() { + s.impl.setTemplateIcon(icon) + }) + } + return s +} + +func (s *SystemTray) SetTooltip(tooltip string) { + if s.impl == nil { + s.tooltip = tooltip + return + } + InvokeSync(func() { + s.impl.setTooltip(tooltip) + }) +} + +func (s *SystemTray) Destroy() { + globalApplication.SystemTray.destroy(s) +} + +func (s *SystemTray) destroy() { + if s.impl == nil { + return + } + s.impl.destroy() +} + +func (s *SystemTray) OnClick(handler func()) *SystemTray { + s.clickHandler = handler + return s +} + +func (s *SystemTray) OnRightClick(handler func()) *SystemTray { + s.rightClickHandler = handler + return s +} + +func (s *SystemTray) OnDoubleClick(handler func()) *SystemTray { + s.doubleClickHandler = handler + return s +} + +func (s *SystemTray) OnRightDoubleClick(handler func()) *SystemTray { + s.rightDoubleClickHandler = handler + return s +} + +func (s *SystemTray) OnMouseEnter(handler func()) *SystemTray { + s.mouseEnterHandler = handler + return s +} + +func (s *SystemTray) OnMouseLeave(handler func()) *SystemTray { + s.mouseLeaveHandler = handler + return s +} + +func (s *SystemTray) Show() { + if s.impl == nil { + return + } + InvokeSync(func() { + s.impl.Show() + }) +} + +func (s *SystemTray) Hide() { + if s.impl == nil { + return + } + InvokeSync(func() { + s.impl.Hide() + }) +} + +type WindowAttachConfig struct { + // Window is the window to attach to the system tray. If it's null, the request to attach will be ignored. + Window Window + + // Offset indicates the gap in pixels between the system tray and the window + Offset int + + // Debounce is used by Windows to indicate how long to wait before responding to a mouse + // up event on the notification icon. See https://stackoverflow.com/questions/4585283/alternate-showing-hiding-window-when-notify-icon-is-clicked + Debounce time.Duration + + // Indicates that the window has just been closed + justClosed bool + + // Indicates that the window has been shown a first time + hasBeenShown bool + + // Used to ensure that the window state is read on first click + initialClick sync.Once +} + +// AttachWindow attaches a window to the system tray. The window will be shown when the system tray icon is clicked. +// The window will be hidden when the system tray icon is clicked again, or when the window loses focus. +func (s *SystemTray) AttachWindow(window Window) *SystemTray { + s.attachedWindow.Window = window + return s +} + +// WindowOffset sets the gap in pixels between the system tray and the window +func (s *SystemTray) WindowOffset(offset int) *SystemTray { + s.attachedWindow.Offset = offset + return s +} + +// WindowDebounce is used by Windows to indicate how long to wait before responding to a mouse +// up event on the notification icon. This prevents the window from being hidden and then immediately +// shown when the user clicks on the system tray icon. +// See https://stackoverflow.com/questions/4585283/alternate-showing-hiding-window-when-notify-icon-is-clicked +func (s *SystemTray) WindowDebounce(debounce time.Duration) *SystemTray { + s.attachedWindow.Debounce = debounce + return s +} + +func (s *SystemTray) defaultClickHandler() { + if s.attachedWindow.Window == nil { + s.OpenMenu() + return + } + + // Check the initial visibility state + s.attachedWindow.initialClick.Do(func() { + s.attachedWindow.hasBeenShown = s.attachedWindow.Window.IsVisible() + }) + + if runtime.GOOS == "windows" && s.attachedWindow.justClosed { + return + } + + if s.attachedWindow.Window.IsVisible() { + s.attachedWindow.Window.Hide() + } else { + s.attachedWindow.hasBeenShown = true + _ = s.PositionWindow(s.attachedWindow.Window, s.attachedWindow.Offset) + s.attachedWindow.Window.Show().Focus() + } +} + +func (s *SystemTray) OpenMenu() { + if s.menu == nil { + return + } + if s.impl == nil { + return + } + InvokeSync(s.impl.openMenu) +} diff --git a/v3/pkg/application/systemtray_darwin.go b/v3/pkg/application/systemtray_darwin.go new file mode 100644 index 000000000..ed2e22ad7 --- /dev/null +++ b/v3/pkg/application/systemtray_darwin.go @@ -0,0 +1,287 @@ +//go:build darwin + +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa -framework WebKit + +#include "Cocoa/Cocoa.h" +#include "menuitem_darwin.h" +#include "systemtray_darwin.h" + +// Show the system tray icon +static void systemTrayShow(void* nsStatusItem) { + dispatch_async(dispatch_get_main_queue(), ^{ + // Get the NSStatusItem + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + [statusItem setVisible:YES]; + }); +} + +// Hide the system tray icon +static void systemTrayHide(void* nsStatusItem) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + [statusItem setVisible:NO]; + }); +} + +*/ +import "C" +import ( + "errors" + "unsafe" + + "github.com/leaanthony/go-ansi-parser" +) + +type macosSystemTray struct { + id uint + label string + icon []byte + menu *Menu + + nsStatusItem unsafe.Pointer + nsImage unsafe.Pointer + nsMenu unsafe.Pointer + iconPosition IconPosition + isTemplateIcon bool + parent *SystemTray + lastClickedScreen unsafe.Pointer +} + +func (s *macosSystemTray) Show() { + if s.nsStatusItem == nil { + return + } + C.systemTrayShow(s.nsStatusItem) +} + +func (s *macosSystemTray) Hide() { + if s.nsStatusItem == nil { + return + } + C.systemTrayHide(s.nsStatusItem) +} + +func (s *macosSystemTray) openMenu() { + if s.nsMenu == nil { + return + } + C.showMenu(s.nsStatusItem, s.nsMenu) +} + +type button int + +const ( + leftButtonDown button = 1 + rightButtonDown button = 3 +) + +// system tray map +var systemTrayMap = make(map[uint]*macosSystemTray) + +//export systrayClickCallback +func systrayClickCallback(id C.long, buttonID C.int) { + // Get the system tray + systemTray := systemTrayMap[uint(id)] + if systemTray == nil { + globalApplication.error("system tray not found: %v", id) + return + } + systemTray.processClick(button(buttonID)) +} + +func (s *macosSystemTray) setIconPosition(position IconPosition) { + s.iconPosition = position +} + +func (s *macosSystemTray) setMenu(menu *Menu) { + s.menu = menu +} + +func (s *macosSystemTray) positionWindow(window Window, offset int) error { + // Get the platform-specific window implementation + nativeWindow := window.NativeWindow() + if nativeWindow == nil { + return errors.New("window native implementation unavailable") + } + + // Position the window relative to the systray + C.systemTrayPositionWindow(s.nsStatusItem, nativeWindow, C.int(offset)) + + return nil +} + +func (s *macosSystemTray) getScreen() (*Screen, error) { + if s.lastClickedScreen != nil { + // Get the screen frame + frame := C.NSScreen_frame(s.lastClickedScreen) + result := &Screen{ + Bounds: Rect{ + X: int(frame.origin.x), + Y: int(frame.origin.y), + Width: int(frame.size.width), + Height: int(frame.size.height), + }, + } + return result, nil + } + return nil, errors.New("no screen available") +} + +func (s *macosSystemTray) bounds() (*Rect, error) { + var rect C.NSRect + var screen unsafe.Pointer + C.systemTrayGetBounds(s.nsStatusItem, &rect, &screen) + + // Store the screen for use in positionWindow + s.lastClickedScreen = screen + + // Return the screen-relative coordinates + result := &Rect{ + X: int(rect.origin.x), + Y: int(rect.origin.y), + Width: int(rect.size.width), + Height: int(rect.size.height), + } + return result, nil +} + +func (s *macosSystemTray) run() { + globalApplication.dispatchOnMainThread(func() { + if s.nsStatusItem != nil { + Fatal("System tray '%d' already running", s.id) + } + s.nsStatusItem = unsafe.Pointer(C.systemTrayNew(C.long(s.id))) + + if s.label != "" { + s.setLabel(s.label) + } + if s.icon != nil { + s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&s.icon[0]), C.int(len(s.icon)))) + C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + } + if s.menu != nil { + s.menu.Update() + // Convert impl to macosMenu object + s.nsMenu = (s.menu.impl).(*macosMenu).nsMenu + } + }) +} + +func (s *macosSystemTray) setIcon(icon []byte) { + s.icon = icon + globalApplication.dispatchOnMainThread(func() { + s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) + C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + }) +} + +func (s *macosSystemTray) setDarkModeIcon(icon []byte) { + s.setIcon(icon) +} + +func (s *macosSystemTray) setTemplateIcon(icon []byte) { + s.icon = icon + s.isTemplateIcon = true + globalApplication.dispatchOnMainThread(func() { + s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) + C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + }) +} + +func (s *macosSystemTray) setTooltip(tooltip string) { + // Tooltips not supported on macOS +} + +func newSystemTrayImpl(s *SystemTray) systemTrayImpl { + result := &macosSystemTray{ + parent: s, + id: s.id, + label: s.label, + icon: s.icon, + menu: s.menu, + iconPosition: s.iconPosition, + isTemplateIcon: s.isTemplateIcon, + } + systemTrayMap[s.id] = result + return result +} + +func extractAnsiTextParts(text *ansi.StyledText) (label *C.char, fg *C.char, bg *C.char) { + label = C.CString(text.Label) + if text.FgCol != nil { + fg = C.CString(text.FgCol.Hex) + } + if text.BgCol != nil { + bg = C.CString(text.BgCol.Hex) + } + return +} + +func (s *macosSystemTray) setLabel(label string) { + s.label = label + if !ansi.HasEscapeCodes(label) { + C.systemTraySetLabel(s.nsStatusItem, C.CString(label)) + } else { + parsed, err := ansi.Parse(label) + if err != nil { + C.systemTraySetLabel(s.nsStatusItem, C.CString(label)) + return + } + if len(parsed) == 0 { + return + } + label, fg, bg := extractAnsiTextParts(parsed[0]) + var attributedString = C.createAttributedString(label, fg, bg) + if len(parsed) > 1 { + for _, parsedPart := range parsed[1:] { + label, fg, bg = extractAnsiTextParts(parsedPart) + attributedString = C.appendAttributedString(attributedString, label, fg, bg) + } + } + + C.systemTraySetANSILabel(s.nsStatusItem, attributedString) + } +} + +func (s *macosSystemTray) destroy() { + // Remove the status item from the status bar and its associated menu + C.systemTrayDestroy(s.nsStatusItem) +} + +func (s *macosSystemTray) processClick(b button) { + switch b { + case leftButtonDown: + // Check if we have a callback + if s.parent.clickHandler != nil { + s.parent.clickHandler() + return + } + if s.parent.attachedWindow.Window != nil { + s.parent.defaultClickHandler() + return + } + if s.menu != nil { + C.showMenu(s.nsStatusItem, s.nsMenu) + } + case rightButtonDown: + // Check if we have a callback + if s.parent.rightClickHandler != nil { + s.parent.rightClickHandler() + return + } + if s.menu != nil { + if s.parent.attachedWindow.Window != nil { + s.parent.attachedWindow.Window.Hide() + } + C.showMenu(s.nsStatusItem, s.nsMenu) + return + } + if s.parent.attachedWindow.Window != nil { + s.parent.defaultClickHandler() + } + } +} diff --git a/v3/pkg/application/systemtray_darwin.h b/v3/pkg/application/systemtray_darwin.h new file mode 100644 index 000000000..bd1940237 --- /dev/null +++ b/v3/pkg/application/systemtray_darwin.h @@ -0,0 +1,24 @@ +//go:build darwin + +#include + +@interface StatusItemController : NSObject +@property long id; +- (void)statusItemClicked:(id)sender; +@end + +void* systemTrayNew(long id); +void systemTraySetLabel(void* nsStatusItem, char *label); +void systemTraySetANSILabel(void* nsStatusItem, void* attributedString); +void systemTraySetLabelColor(void* nsStatusItem, char *fg, char *bg); +void* createAttributedString(char *title, char *FG, char *BG); +void* appendAttributedString(void* original, char* label, char* fg, char* bg); +NSImage* imageFromBytes(const unsigned char *bytes, int length); +void systemTraySetIcon(void* nsStatusItem, void* nsImage, int position, bool isTemplate); +void systemTrayDestroy(void* nsStatusItem); +void showMenu(void* nsStatusItem, void *nsMenu); +void systemTrayGetBounds(void* nsStatusItem, NSRect *rect, void **screen); +NSRect NSScreen_frame(void* screen); +void windowSetScreen(void* window, void* screen, int yOffset); +int statusBarHeight(); +void systemTrayPositionWindow(void* nsStatusItem, void* nsWindow, int offset); \ No newline at end of file diff --git a/v3/pkg/application/systemtray_darwin.m b/v3/pkg/application/systemtray_darwin.m new file mode 100644 index 000000000..9a6bb2ccc --- /dev/null +++ b/v3/pkg/application/systemtray_darwin.m @@ -0,0 +1,244 @@ +//go:build darwin + +#include "Cocoa/Cocoa.h" +#include "menuitem_darwin.h" +#include "systemtray_darwin.h" + +extern void systrayClickCallback(long, int); + +// StatusItemController.m +@implementation StatusItemController + +- (void)statusItemClicked:(id)sender { + NSEvent *event = [NSApp currentEvent]; + systrayClickCallback(self.id, event.type); +} + +@end + +// Create a new system tray +void* systemTrayNew(long id) { + StatusItemController *controller = [[StatusItemController alloc] init]; + controller.id = id; + NSStatusItem *statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain]; + [statusItem setTarget:controller]; + [statusItem setAction:@selector(statusItemClicked:)]; + NSButton *button = statusItem.button; + [button sendActionOn:(NSEventMaskLeftMouseDown|NSEventMaskRightMouseDown)]; + return (void*)statusItem; +} + +void systemTraySetLabel(void* nsStatusItem, char *label) { + if( label == NULL ) { + return; + } + // Set the label on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + statusItem.button.title = [NSString stringWithUTF8String:label]; + free(label); + }); +} + +void systemTraySetANSILabel(void* nsStatusItem, void* label) { + if( label == NULL ) { + return; + } + + NSMutableAttributedString* attributedString = (NSMutableAttributedString*) label; + + // Set the label + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + [statusItem setAttributedTitle:attributedString]; + // [attributedString release]; +} + +void* appendAttributedString(void *currentString, char *title, char *FG, char *BG) { + + NSMutableAttributedString* newString = createAttributedString(title, FG, BG); + if( currentString != NULL ) { + NSMutableAttributedString* current = (NSMutableAttributedString*)currentString; + [current appendAttributedString:newString]; + newString = current; + } + + return (void*)newString; +} + +void* createAttributedString(char *title, char *FG, char *BG) { + + NSMutableDictionary *dictionary = [NSMutableDictionary new]; + + // RGBA + if(FG != NULL && strlen(FG) > 0) { + unsigned short r, g, b, a; + + // white by default + r = g = b = a = 255; + int count = sscanf(FG, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a); + if (count > 0) { + NSColor *colour = [NSColor colorWithCalibratedRed:(CGFloat)r / 255.0 + green:(CGFloat)g / 255.0 + blue:(CGFloat)b / 255.0 + alpha:(CGFloat)a / 255.0]; + dictionary[NSForegroundColorAttributeName] = colour; + + } + } + + // Calculate BG colour + if(BG != NULL && strlen(BG) > 0) { + unsigned short r, g, b, a; + + // white by default + r = g = b = a = 255; + int count = sscanf(BG, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a); + if (count > 0) { + NSColor *colour = [NSColor colorWithCalibratedRed:(CGFloat)r / 255.0 + green:(CGFloat)g / 255.0 + blue:(CGFloat)b / 255.0 + alpha:(CGFloat)a / 255.0]; + dictionary[NSBackgroundColorAttributeName] = colour; + } + } + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithUTF8String:title] attributes:dictionary]; + return (void*)attributedString; +} + +// Create an nsimage from a byte array +NSImage* imageFromBytes(const unsigned char *bytes, int length) { + NSData *data = [NSData dataWithBytes:bytes length:length]; + NSImage *image = [[NSImage alloc] initWithData:data]; + return image; +} + +// Set the icon on the system tray +void systemTraySetIcon(void* nsStatusItem, void* nsImage, int position, bool isTemplate) { + // Set the icon on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + NSImage *image = (NSImage *)nsImage; + + NSStatusBar *statusBar = [NSStatusBar systemStatusBar]; + CGFloat thickness = [statusBar thickness]; + [image setSize:NSMakeSize(thickness, thickness)]; + if( isTemplate ) { + [image setTemplate:YES]; + } + statusItem.button.image = [image autorelease]; + statusItem.button.imagePosition = position; + }); +} + +// Destroy system tray +void systemTrayDestroy(void* nsStatusItem) { + // Remove the status item from the status bar and its associated menu + dispatch_async(dispatch_get_main_queue(), ^{ + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; + [statusItem release]; + }); +} + +void showMenu(void* nsStatusItem, void *nsMenu) { + // Show the menu on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + [statusItem popUpStatusItemMenu:(NSMenu *)nsMenu]; + // Post a mouse up event so the statusitem defocuses + NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseUp + location:[NSEvent mouseLocation] + modifierFlags:0 + timestamp:[[NSProcessInfo processInfo] systemUptime] + windowNumber:0 + context:nil + eventNumber:0 + clickCount:1 + pressure:1]; + [NSApp postEvent:event atStart:NO]; + [statusItem.button highlight:NO]; + }); +} + +void systemTrayGetBounds(void* nsStatusItem, NSRect *rect, void **outScreen) { + NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem; + NSStatusBarButton *button = statusItem.button; + + // Get mouse location and find the screen it's on + NSPoint mouseLocation = [NSEvent mouseLocation]; + NSScreen *screen = nil; + NSArray *screens = [NSScreen screens]; + + for (NSScreen *candidate in screens) { + NSRect frame = [candidate frame]; + if (NSPointInRect(mouseLocation, frame)) { + screen = candidate; + break; + } + } + if (!screen) { + screen = [NSScreen mainScreen]; + } + + // Get button frame in screen coordinates + NSRect buttonFrame = button.frame; + NSRect buttonFrameScreen = [button.window convertRectToScreen:buttonFrame]; + + *rect = buttonFrameScreen; + *outScreen = (void*)screen; +} + +NSRect NSScreen_frame(void* screen) { + return [(NSScreen*)screen frame]; +} + +int statusBarHeight() { + NSMenu *mainMenu = [NSApp mainMenu]; + CGFloat menuBarHeight = [mainMenu menuBarHeight]; + return (int)menuBarHeight; +} + +void systemTrayPositionWindow(void* nsStatusItem, void* nsWindow, int offset) { + // Get the status item's button + NSStatusBarButton *button = [(NSStatusItem*)nsStatusItem button]; + + // Get the frame in screen coordinates + NSRect frame = [button.window convertRectToScreen:button.frame]; + + // Get the screen that contains the status item + NSScreen *screen = [button.window screen]; + if (screen == nil) { + screen = [NSScreen mainScreen]; + } + + // Get screen's backing scale factor (DPI) + CGFloat scaleFactor = [screen backingScaleFactor]; + + // Get the window's frame + NSRect windowFrame = [(NSWindow*)nsWindow frame]; + + // Calculate the horizontal position (centered under the status item) + CGFloat windowX = frame.origin.x + (frame.size.width - windowFrame.size.width) / 2; + + // If the window would go off the right edge of the screen, adjust it + if (windowX + windowFrame.size.width > screen.frame.origin.x + screen.frame.size.width) { + windowX = screen.frame.origin.x + screen.frame.size.width - windowFrame.size.width; + } + // If the window would go off the left edge of the screen, adjust it + if (windowX < screen.frame.origin.x) { + windowX = screen.frame.origin.x; + } + + // Get screen metrics + NSRect screenFrame = [screen frame]; + NSRect visibleFrame = [screen visibleFrame]; + + // Calculate the vertical position + CGFloat scaledOffset = offset * scaleFactor; + CGFloat windowY = visibleFrame.origin.y + visibleFrame.size.height - windowFrame.size.height - scaledOffset; + + // Set the window's frame + windowFrame.origin.x = windowX; + windowFrame.origin.y = windowY; + [(NSWindow*)nsWindow setFrame:windowFrame display:YES animate:NO]; +} diff --git a/v3/pkg/application/systemtray_linux.go b/v3/pkg/application/systemtray_linux.go new file mode 100644 index 000000000..ea139b636 --- /dev/null +++ b/v3/pkg/application/systemtray_linux.go @@ -0,0 +1,760 @@ +//go:build linux + +/* +Portions of this code are derived from the project: +- https://github.com/fyne-io/systray +*/ +package application + +import "C" +import ( + "fmt" + "os" + + "github.com/godbus/dbus/v5" + "github.com/godbus/dbus/v5/introspect" + "github.com/godbus/dbus/v5/prop" + "github.com/wailsapp/wails/v3/internal/dbus/menu" + "github.com/wailsapp/wails/v3/internal/dbus/notifier" + "github.com/wailsapp/wails/v3/pkg/icons" +) + +const ( + itemPath = "/StatusNotifierItem" + menuPath = "/StatusNotifierMenu" +) + +type linuxSystemTray struct { + parent *SystemTray + + id uint + label string + icon []byte + menu *Menu + + iconPosition IconPosition + isTemplateIcon bool + + quitChan chan struct{} + conn *dbus.Conn + props *prop.Properties + menuProps *prop.Properties + + menuVersion uint32 // need to bump this anytime we change anything + itemMap map[int32]*systrayMenuItem + tooltip string +} + +func (s *linuxSystemTray) getScreen() (*Screen, error) { + _, _, result := getMousePosition() + return result, nil +} + +// dbusMenu is a named struct to map into generated bindings. +// It represents the layout of a menu item +type dbusMenu = struct { + V0 int32 // items' unique id + V1 map[string]dbus.Variant // layout properties + V2 []dbus.Variant // child menu(s) +} + +// systrayMenuItem is an implementation of the menuItemImpl interface +type systrayMenuItem struct { + sysTray *linuxSystemTray + menuItem *MenuItem + dbusItem *dbusMenu +} + +func (s *systrayMenuItem) setBitmap(data []byte) { + s.dbusItem.V1["icon-data"] = dbus.MakeVariant(data) + s.sysTray.update(s) +} + +func (s *systrayMenuItem) setTooltip(v string) { + s.dbusItem.V1["tooltip"] = dbus.MakeVariant(v) + s.sysTray.update(s) +} + +func (s *systrayMenuItem) setLabel(v string) { + s.dbusItem.V1["label"] = dbus.MakeVariant(v) + s.sysTray.update(s) +} + +func (s *systrayMenuItem) setDisabled(disabled bool) { + v := dbus.MakeVariant(!disabled) + if s.dbusItem.V1["toggle-state"] != v { + s.dbusItem.V1["enabled"] = v + s.sysTray.update(s) + } +} + +func (s *systrayMenuItem) destroy() {} + +func (s *systrayMenuItem) setChecked(checked bool) { + v := dbus.MakeVariant(0) + if checked { + v = dbus.MakeVariant(1) + } + if s.dbusItem.V1["toggle-state"] != v { + s.dbusItem.V1["toggle-state"] = v + s.sysTray.update(s) + } +} + +func (s *systrayMenuItem) setAccelerator(accelerator *accelerator) {} +func (s *systrayMenuItem) setHidden(hidden bool) { + s.dbusItem.V1["visible"] = dbus.MakeVariant(!hidden) + s.sysTray.update(s) +} + +func (s *systrayMenuItem) dbus() *dbusMenu { + item := &dbusMenu{ + V0: int32(s.menuItem.id), + V1: map[string]dbus.Variant{}, + V2: []dbus.Variant{}, + } + return item +} + +func (s *linuxSystemTray) setIconPosition(position IconPosition) { + s.iconPosition = position +} + +func (s *linuxSystemTray) processMenu(menu *Menu, parentId int32) { + parentItem, ok := s.itemMap[int32(parentId)] + if !ok { + return + } + parent := parentItem.dbusItem + + for _, item := range menu.items { + menuItem := &dbusMenu{ + V0: int32(item.id), + V1: map[string]dbus.Variant{}, + V2: []dbus.Variant{}, + } + item.impl = &systrayMenuItem{ + sysTray: s, + menuItem: item, + dbusItem: menuItem, + } + s.itemMap[int32(item.id)] = item.impl.(*systrayMenuItem) + + menuItem.V1["enabled"] = dbus.MakeVariant(!item.disabled) + menuItem.V1["visible"] = dbus.MakeVariant(!item.hidden) + if item.label != "" { + menuItem.V1["label"] = dbus.MakeVariant(item.label) + } + if item.bitmap != nil { + menuItem.V1["icon-data"] = dbus.MakeVariant(item.bitmap) + } + switch item.itemType { + case checkbox: + menuItem.V1["toggle-type"] = dbus.MakeVariant("checkmark") + v := dbus.MakeVariant(0) + if item.checked { + v = dbus.MakeVariant(1) + } + menuItem.V1["toggle-state"] = v + case submenu: + menuItem.V1["children-display"] = dbus.MakeVariant("submenu") + s.processMenu(item.submenu, int32(item.id)) + case text: + case radio: + menuItem.V1["toggle-type"] = dbus.MakeVariant("radio") + v := dbus.MakeVariant(0) + if item.checked { + v = dbus.MakeVariant(1) + } + menuItem.V1["toggle-state"] = v + case separator: + menuItem.V1["type"] = dbus.MakeVariant("separator") + } + + parent.V2 = append(parent.V2, dbus.MakeVariant(menuItem)) + } +} + +func (s *linuxSystemTray) refresh() { + s.menuVersion++ + if err := s.menuProps.Set("com.canonical.dbusmenu", "Version", + dbus.MakeVariant(s.menuVersion)); err != nil { + globalApplication.error("systray error: failed to update menu version: %w", err) + return + } + if err := menu.Emit(s.conn, &menu.Dbusmenu_LayoutUpdatedSignal{ + Path: menuPath, + Body: &menu.Dbusmenu_LayoutUpdatedSignalBody{ + Revision: s.menuVersion, + }, + }); err != nil { + globalApplication.error("systray error: failed to emit layout updated signal: %w", err) + } +} + +func (s *linuxSystemTray) setMenu(menu *Menu) { + if s.parent.attachedWindow.Window != nil { + temp := menu + menu = NewMenu() + title := "Open" + if s.parent.attachedWindow.Window.Name() != "" { + title += " " + s.parent.attachedWindow.Window.Name() + } else { + title += " window" + } + openMenuItem := menu.Add(title) + openMenuItem.OnClick(func(*Context) { + s.parent.clickHandler() + }) + menu.AddSeparator() + menu.Append(temp) + } + s.itemMap = map[int32]*systrayMenuItem{} + // our root menu element + s.itemMap[0] = &systrayMenuItem{ + menuItem: nil, + dbusItem: &dbusMenu{ + V0: int32(0), + V1: map[string]dbus.Variant{}, + V2: []dbus.Variant{}, + }, + } + menu.processRadioGroups() + s.processMenu(menu, 0) + s.menu = menu +} + +func (s *linuxSystemTray) positionWindow(window Window, offset int) error { + // Get the mouse location on the screen + mouseX, mouseY, currentScreen := getMousePosition() + screenBounds := currentScreen.Size + + // Calculate new X position + newX := mouseX - (window.Width() / 2) + + // Check if the window goes out of the screen bounds on the left side + if newX < 0 { + newX = 0 + } + + // Check if the window goes out of the screen bounds on the right side + if newX+window.Width() > screenBounds.Width { + newX = screenBounds.Width - window.Width() + } + + // Calculate new Y position + newY := mouseY - (window.Height() / 2) + + // Check if the window goes out of the screen bounds on the top + if newY < 0 { + newY = 0 + } + + // Check if the window goes out of the screen bounds on the bottom + if newY+window.Height() > screenBounds.Height { + newY = screenBounds.Height - window.Height() - offset + } + + // Set the new position of the window + window.SetPosition(newX, newY) + return nil +} + +func (s *linuxSystemTray) bounds() (*Rect, error) { + + // Best effort guess at the screen bounds + + return &Rect{}, nil + +} + +func (s *linuxSystemTray) run() { + conn, err := dbus.SessionBus() + if err != nil { + globalApplication.error("systray error: failed to connect to DBus: %w\n", err) + return + } + err = notifier.ExportStatusNotifierItem(conn, itemPath, s) + if err != nil { + globalApplication.error("systray error: failed to export status notifier item: %w\n", err) + } + + err = menu.ExportDbusmenu(conn, menuPath, s) + if err != nil { + globalApplication.error("systray error: failed to export status notifier menu: %w", err) + return + } + + name := fmt.Sprintf("org.kde.StatusNotifierItem-%d-1", os.Getpid()) // register id 1 for this process + _, err = conn.RequestName(name, dbus.NameFlagDoNotQueue) + if err != nil { + globalApplication.error("systray error: failed to request name: %w", err) + // it's not critical error: continue + } + props, err := prop.Export(conn, itemPath, s.createPropSpec()) + if err != nil { + globalApplication.error("systray error: failed to export notifier item properties to bus: %w", err) + return + } + menuProps, err := prop.Export(conn, menuPath, s.createMenuPropSpec()) + if err != nil { + globalApplication.error("systray error: failed to export notifier menu properties to bus: %w", err) + return + } + + s.conn = conn + s.props = props + s.menuProps = menuProps + + node := introspect.Node{ + Name: itemPath, + Interfaces: []introspect.Interface{ + introspect.IntrospectData, + prop.IntrospectData, + notifier.IntrospectDataStatusNotifierItem, + }, + } + err = conn.Export(introspect.NewIntrospectable(&node), itemPath, "org.freedesktop.DBus.Introspectable") + if err != nil { + globalApplication.error("systray error: failed to export node introspection: %w", err) + return + } + menuNode := introspect.Node{ + Name: menuPath, + Interfaces: []introspect.Interface{ + introspect.IntrospectData, + prop.IntrospectData, + menu.IntrospectDataDbusmenu, + }, + } + err = conn.Export(introspect.NewIntrospectable(&menuNode), menuPath, + "org.freedesktop.DBus.Introspectable") + if err != nil { + globalApplication.error("systray error: failed to export menu node introspection: %w", err) + return + } + s.setLabel(s.label) + go func() { + defer handlePanic() + s.register() + + if err := conn.AddMatchSignal( + dbus.WithMatchObjectPath("/org/freedesktop/DBus"), + dbus.WithMatchInterface("org.freedesktop.DBus"), + dbus.WithMatchSender("org.freedesktop.DBus"), + dbus.WithMatchMember("NameOwnerChanged"), + dbus.WithMatchArg(0, "org.kde.StatusNotifierWatcher"), + ); err != nil { + globalApplication.error("systray error: failed to register signal matching: %w", err) + return + } + + sc := make(chan *dbus.Signal, 10) + conn.Signal(sc) + + for { + select { + case sig := <-sc: + if sig == nil { + return // We get a nil signal when closing the window. + } + // sig.Body has the args, which are [name old_owner new_owner] + if sig.Body[2] != "" { + s.register() + } + + case <-s.quitChan: + return + } + } + }() + + if s.parent.label != "" { + s.setLabel(s.parent.label) + } + + if s.parent.tooltip != "" { + s.setTooltip(s.parent.tooltip) + } + s.setMenu(s.menu) +} + +func (s *linuxSystemTray) setTooltip(_ string) { + // TBD +} + +func (s *linuxSystemTray) setIcon(icon []byte) { + + s.icon = icon + + iconPx, err := iconToPX(icon) + if err != nil { + globalApplication.error("systray error: failed to convert icon to PX: %w", err) + return + } + s.props.SetMust("org.kde.StatusNotifierItem", "IconPixmap", []PX{iconPx}) + + if s.conn == nil { + return + } + + err = notifier.Emit(s.conn, ¬ifier.StatusNotifierItem_NewIconSignal{ + Path: itemPath, + Body: ¬ifier.StatusNotifierItem_NewIconSignalBody{}, + }) + if err != nil { + globalApplication.error("systray error: failed to emit new icon signal: %w", err) + return + } +} + +func (s *linuxSystemTray) setDarkModeIcon(icon []byte) { + s.setIcon(icon) +} + +func (s *linuxSystemTray) setTemplateIcon(icon []byte) { + s.icon = icon + s.isTemplateIcon = true + s.setIcon(icon) +} + +func newSystemTrayImpl(s *SystemTray) systemTrayImpl { + label := s.label + if label == "" { + label = "Wails" + } + + return &linuxSystemTray{ + parent: s, + id: s.id, + label: label, + icon: s.icon, + menu: s.menu, + iconPosition: s.iconPosition, + isTemplateIcon: s.isTemplateIcon, + quitChan: make(chan struct{}), + menuVersion: 1, + } +} + +func (s *linuxSystemTray) openMenu() { + // FIXME: Emit com.canonical to open? + globalApplication.info("systray error: openMenu not implemented on Linux") +} + +func (s *linuxSystemTray) setLabel(label string) { + s.label = label + + if err := s.props.Set("org.kde.StatusNotifierItem", "Title", dbus.MakeVariant(label)); err != nil { + globalApplication.error("systray error: failed to set Title prop: %w", err) + return + } + + if s.conn == nil { + return + } + + if err := notifier.Emit(s.conn, ¬ifier.StatusNotifierItem_NewTitleSignal{ + Path: itemPath, + Body: ¬ifier.StatusNotifierItem_NewTitleSignalBody{}, + }); err != nil { + globalApplication.error("systray error: failed to emit new title signal: %w", err) + return + } + +} + +func (s *linuxSystemTray) destroy() { + close(s.quitChan) +} + +func (s *linuxSystemTray) createMenuPropSpec() map[string]map[string]*prop.Prop { + return map[string]map[string]*prop.Prop{ + "com.canonical.dbusmenu": { + // update version each time we change something + "Version": { + Value: s.menuVersion, + Writable: true, + Emit: prop.EmitTrue, + Callback: nil, + }, + "TextDirection": { + Value: "ltr", + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "Status": { + Value: "normal", + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "IconThemePath": { + Value: []string{}, + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + }, + } +} + +func (s *linuxSystemTray) createPropSpec() map[string]map[string]*prop.Prop { + props := map[string]*prop.Prop{ + "Status": { + Value: "Active", // Passive, Active or NeedsAttention + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "Title": { + Value: s.label, + Writable: true, + Emit: prop.EmitTrue, + Callback: nil, + }, + "Id": { + Value: s.label, + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "Category": { + Value: "ApplicationStatus", + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "IconData": { + Value: "", + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + + "IconName": { + Value: "", + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "IconThemePath": { + Value: "", + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "ItemIsMenu": { + Value: true, + Writable: false, + Emit: prop.EmitTrue, + Callback: nil, + }, + "Menu": { + Value: dbus.ObjectPath(menuPath), + Writable: true, + Emit: prop.EmitTrue, + Callback: nil, + }, + "ToolTip": { + Value: tooltip{V2: s.label}, + Writable: true, + Emit: prop.EmitTrue, + Callback: nil, + }, + } + + if s.icon == nil { + // set a basic default one if one isn't set + s.icon = icons.WailsLogoWhiteTransparent + } + if iconPx, err := iconToPX(s.icon); err == nil { + props["IconPixmap"] = &prop.Prop{ + Value: []PX{iconPx}, + Writable: true, + Emit: prop.EmitTrue, + Callback: nil, + } + } + + return map[string]map[string]*prop.Prop{ + "org.kde.StatusNotifierItem": props, + } +} + +func (s *linuxSystemTray) update(i *systrayMenuItem) { + s.itemMap[int32(i.menuItem.id)] = i + s.refresh() +} + +func (s *linuxSystemTray) register() bool { + obj := s.conn.Object("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher") + call := obj.Call("org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem", 0, itemPath) + if call.Err != nil { + globalApplication.error("systray error: failed to register: %w", call.Err) + return false + } + + return true +} + +type PX struct { + W, H int + Pix []byte +} + +func iconToPX(icon []byte) (PX, error) { + img, err := pngToImage(icon) + if err != nil { + return PX{}, err + } + w, h, bytes := ToARGB(img) + return PX{ + W: w, + H: h, + Pix: bytes, + }, nil +} + +// AboutToShow is an implementation of the com.canonical.dbusmenu.AboutToShow method. +func (s *linuxSystemTray) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) { + return +} + +// AboutToShowGroup is an implementation of the com.canonical.dbusmenu.AboutToShowGroup method. +func (s *linuxSystemTray) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) { + return +} + +// GetProperty is an implementation of the com.canonical.dbusmenu.GetProperty method. +func (s *linuxSystemTray) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) { + if item, ok := s.itemMap[id]; ok { + if p, ok := item.dbusItem.V1[name]; ok { + return p, nil + } + } + return +} + +// Event is com.canonical.dbusmenu.Event method. +func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, timestamp uint32) (err *dbus.Error) { + switch eventID { + case "clicked": + if item, ok := s.itemMap[id]; ok { + InvokeAsync(item.menuItem.handleClick) + } + case "opened": + if s.parent.clickHandler != nil { + s.parent.clickHandler() + } + if s.parent.onMenuOpen != nil { + s.parent.onMenuOpen() + } + case "closed": + if s.parent.onMenuClose != nil { + s.parent.onMenuClose() + } + } + return +} + +// EventGroup is an implementation of the com.canonical.dbusmenu.EventGroup method. +func (s *linuxSystemTray) EventGroup(events []struct { + V0 int32 + V1 string + V2 dbus.Variant + V3 uint32 +}) (idErrors []int32, err *dbus.Error) { + for _, event := range events { + fmt.Printf("EventGroup: %v, %v, %v, %v\n", event.V0, event.V1, event.V2, event.V3) + if event.V1 == "clicked" { + item, ok := s.itemMap[event.V0] + if ok { + InvokeAsync(item.menuItem.handleClick) + } + } + } + return +} + +// GetGroupProperties is an implementation of the com.canonical.dbusmenu.GetGroupProperties method. +func (s *linuxSystemTray) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct { + V0 int32 + V1 map[string]dbus.Variant +}, err *dbus.Error) { + // FIXME: RLock? + /* instance.menuLock.Lock() + defer instance.menuLock.Unlock() + */ + for _, id := range ids { + if m, ok := s.itemMap[id]; ok { + p := struct { + V0 int32 + V1 map[string]dbus.Variant + }{ + V0: m.dbusItem.V0, + V1: make(map[string]dbus.Variant, len(m.dbusItem.V1)), + } + for k, v := range m.dbusItem.V1 { + p.V1[k] = v + } + properties = append(properties, p) + } + } + return properties, nil +} + +// GetLayout is an implementation of the com.canonical.dbusmenu.GetLayout method. +func (s *linuxSystemTray) GetLayout(parentID int32, recursionDepth int32, propertyNames []string) (revision uint32, layout dbusMenu, err *dbus.Error) { + // FIXME: RLock? + if m, ok := s.itemMap[parentID]; ok { + return s.menuVersion, *m.dbusItem, nil + } + + return +} + +// Activate implements org.kde.StatusNotifierItem.Activate method. +func (s *linuxSystemTray) Activate(x int32, y int32) (err *dbus.Error) { + if s.parent.doubleClickHandler != nil { + s.parent.doubleClickHandler() + } + return +} + +// ContextMenu is org.kde.StatusNotifierItem.ContextMenu method +func (s *linuxSystemTray) ContextMenu(x int32, y int32) (err *dbus.Error) { + fmt.Println("ContextMenu", x, y) + return nil +} + +func (s *linuxSystemTray) Scroll(delta int32, orientation string) (err *dbus.Error) { + fmt.Println("Scroll", delta, orientation) + return +} + +// SecondaryActivate implements org.kde.StatusNotifierItem.SecondaryActivate method. +func (s *linuxSystemTray) SecondaryActivate(x int32, y int32) (err *dbus.Error) { + s.parent.rightClickHandler() + return +} + +// Show is a no-op for Linux +func (s *linuxSystemTray) Show() { + // No-op +} + +// Hide is a no-op for Linux +func (s *linuxSystemTray) Hide() { + // No-op +} + +// tooltip is our data for a tooltip property. +// Param names need to match the generated code... +type tooltip = struct { + V0 string // name + V1 []PX // icons + V2 string // title + V3 string // description +} diff --git a/v3/pkg/application/systemtray_windows.go b/v3/pkg/application/systemtray_windows.go new file mode 100644 index 000000000..19c7f3509 --- /dev/null +++ b/v3/pkg/application/systemtray_windows.go @@ -0,0 +1,514 @@ +//go:build windows + +package application + +import ( + "errors" + "syscall" + "time" + "unsafe" + + "github.com/wailsapp/wails/v3/pkg/icons" + + "github.com/samber/lo" + + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/w32" +) + +const ( + WM_USER_SYSTRAY = w32.WM_USER + 1 +) + +type windowsSystemTray struct { + parent *SystemTray + + menu *Win32Menu + + // Platform specific implementation + uid uint32 + hwnd w32.HWND + lightModeIcon w32.HICON + darkModeIcon w32.HICON + currentIcon w32.HICON +} + +func (s *windowsSystemTray) openMenu() { + if s.menu == nil { + return + } + // Get the system tray bounds + trayBounds, err := s.bounds() + if err != nil { + return + } + if trayBounds == nil { + return + } + + // Show the menu at the tray bounds + s.menu.ShowAt(trayBounds.X, trayBounds.Y) +} + +func (s *windowsSystemTray) positionWindow(window Window, offset int) error { + // Get the current screen trayBounds + currentScreen, err := s.getScreen() + if err != nil { + return err + } + + screenBounds := currentScreen.WorkArea + windowBounds := window.Bounds() + + newX := screenBounds.Width - windowBounds.Width - offset + newY := screenBounds.Height - windowBounds.Height - offset + + // systray icons in windows can either be in the taskbar + // or in a flyout menu. + var iconIsInTrayBounds bool + iconIsInTrayBounds, err = s.iconIsInTrayBounds() + if err != nil { + return err + } + + var trayBounds *Rect + var centerAlignX, centerAlignY int + + // we only need the traybounds if the icon is in the tray + if iconIsInTrayBounds { + trayBounds, err = s.bounds() + if err != nil { + return err + } + if trayBounds == nil { + return errors.New("failed to get system tray bounds") + } + *trayBounds = PhysicalToDipRect(*trayBounds) + centerAlignX = trayBounds.X + (trayBounds.Width / 2) - (windowBounds.Width / 2) + centerAlignY = trayBounds.Y + (trayBounds.Height / 2) - (windowBounds.Height / 2) + } + + taskbarBounds := w32.GetTaskbarPosition() + + // Set the window position based on the icon location + // if the icon is in the taskbar (traybounds) then we need + // to adjust the position so the window is centered on the icon + switch taskbarBounds.UEdge { + case w32.ABE_LEFT: + if iconIsInTrayBounds && centerAlignY <= newY { + newY = centerAlignY + } + newX = screenBounds.X + offset + case w32.ABE_TOP: + if iconIsInTrayBounds && centerAlignX <= newX { + newX = centerAlignX + } + newY = screenBounds.Y + offset + case w32.ABE_RIGHT: + if iconIsInTrayBounds && centerAlignY <= newY { + newY = centerAlignY + } + case w32.ABE_BOTTOM: + if iconIsInTrayBounds && centerAlignX <= newX { + newX = centerAlignX + } + } + newPos := currentScreen.relativeToAbsoluteDipPoint(Point{X: newX, Y: newY}) + windowBounds.X = newPos.X + windowBounds.Y = newPos.Y + window.SetBounds(windowBounds) + return nil +} + +func (s *windowsSystemTray) bounds() (*Rect, error) { + if s.hwnd == 0 { + return nil, errors.New("system tray window handle not initialized") + } + + bounds, err := w32.GetSystrayBounds(s.hwnd, s.uid) + if err != nil { + return nil, err + } + if bounds == nil { + return nil, errors.New("GetSystrayBounds returned nil") + } + + monitor := w32.MonitorFromWindow(s.hwnd, w32.MONITOR_DEFAULTTONEAREST) + if monitor == 0 { + return nil, errors.New("failed to get monitor") + } + + return &Rect{ + X: int(bounds.Left), + Y: int(bounds.Top), + Width: int(bounds.Right - bounds.Left), + Height: int(bounds.Bottom - bounds.Top), + }, nil +} + +func (s *windowsSystemTray) iconIsInTrayBounds() (bool, error) { + if s.hwnd == 0 { + return false, errors.New("system tray window handle not initialized") + } + + bounds, err := w32.GetSystrayBounds(s.hwnd, s.uid) + if err != nil { + return false, err + } + if bounds == nil { + return false, errors.New("GetSystrayBounds returned nil") + } + + taskbarRect := w32.GetTaskbarPosition() + if taskbarRect == nil { + return false, errors.New("failed to get taskbar position") + } + + inTasksBar := w32.RectInRect(bounds, &taskbarRect.Rc) + if inTasksBar { + return true, nil + } + + return false, nil +} + +func (s *windowsSystemTray) getScreen() (*Screen, error) { + if s.hwnd == 0 { + return nil, errors.New("system tray window handle not initialized") + } + // Get the screen for this systray + return getScreenForWindowHwnd(s.hwnd) +} + +func (s *windowsSystemTray) setMenu(menu *Menu) { + s.updateMenu(menu) +} + +func (s *windowsSystemTray) run() { + s.hwnd = w32.CreateWindowEx( + 0, + w32.MustStringToUTF16Ptr(globalApplication.options.Windows.WndClass), + nil, + 0, + 0, + 0, + 0, + 0, + w32.HWND_MESSAGE, + 0, + 0, + nil) + if s.hwnd == 0 { + panic(syscall.GetLastError()) + } + + nid := w32.NOTIFYICONDATA{ + HWnd: s.hwnd, + UID: uint32(s.parent.id), + UFlags: w32.NIF_ICON | w32.NIF_MESSAGE, + HIcon: s.currentIcon, + UCallbackMessage: WM_USER_SYSTRAY, + } + nid.CbSize = uint32(unsafe.Sizeof(nid)) + + for retries := range 6 { + if !w32.ShellNotifyIcon(w32.NIM_ADD, &nid) { + if retries == 5 { + globalApplication.fatal("failed to register system tray icon: %w", syscall.GetLastError()) + } + + time.Sleep(500 * time.Millisecond) + continue + } + break + } + + nid.UVersion = w32.NOTIFYICON_VERSION + + if !w32.ShellNotifyIcon(w32.NIM_SETVERSION, &nid) { + panic(syscall.GetLastError()) + } + + // Get the application icon if available + defaultIcon := w32.LoadIconWithResourceID(w32.GetModuleHandle(""), w32.RT_ICON) + if defaultIcon != 0 { + s.lightModeIcon = defaultIcon + s.darkModeIcon = defaultIcon + } else { + s.lightModeIcon = lo.Must(w32.CreateSmallHIconFromImage(icons.SystrayLight)) + s.darkModeIcon = lo.Must(w32.CreateSmallHIconFromImage(icons.SystrayDark)) + } + + // Use custom icons if provided + if s.parent.icon != nil { + // Create a new icon and destroy the old one + newIcon := lo.Must(w32.CreateSmallHIconFromImage(s.parent.icon)) + if s.lightModeIcon != 0 && s.lightModeIcon != defaultIcon { + w32.DestroyIcon(s.lightModeIcon) + } + s.lightModeIcon = newIcon + } + if s.parent.darkModeIcon != nil { + // Create a new icon and destroy the old one + newIcon := lo.Must(w32.CreateSmallHIconFromImage(s.parent.darkModeIcon)) + if s.darkModeIcon != 0 && s.darkModeIcon != defaultIcon && s.darkModeIcon != s.lightModeIcon { + w32.DestroyIcon(s.darkModeIcon) + } + s.darkModeIcon = newIcon + } + s.uid = nid.UID + + if s.parent.menu != nil { + s.updateMenu(s.parent.menu) + } + + if s.parent.tooltip != "" { + s.setTooltip(s.parent.tooltip) + } + + // Set Default Callbacks + if s.parent.clickHandler == nil { + s.parent.clickHandler = func() { + globalApplication.debug("Left Button Clicked") + } + } + if s.parent.rightClickHandler == nil { + s.parent.rightClickHandler = func() { + if s.menu != nil { + s.openMenu() + } + } + } + + // Update the icon + s.updateIcon() + + // Listen for dark mode changes + globalApplication.Event.OnApplicationEvent(events.Windows.SystemThemeChanged, func(event *ApplicationEvent) { + s.updateIcon() + }) + + // Register the system tray + getNativeApplication().registerSystemTray(s) +} + +func (s *windowsSystemTray) updateIcon() { + var newIcon w32.HICON + if w32.IsCurrentlyDarkMode() { + newIcon = s.darkModeIcon + } else { + newIcon = s.lightModeIcon + } + if s.currentIcon == newIcon { + return + } + + // Store the old icon to destroy it after updating + oldIcon := s.currentIcon + + s.currentIcon = newIcon + nid := s.newNotifyIconData() + nid.UFlags = w32.NIF_ICON + if s.currentIcon != 0 { + nid.HIcon = s.currentIcon + } + + if !w32.ShellNotifyIcon(w32.NIM_MODIFY, &nid) { + panic(syscall.GetLastError()) + } + + // Destroy the old icon handle if it exists and is not one of our default icons + if oldIcon != 0 && oldIcon != s.lightModeIcon && oldIcon != s.darkModeIcon { + w32.DestroyIcon(oldIcon) + } +} + +func (s *windowsSystemTray) newNotifyIconData() w32.NOTIFYICONDATA { + nid := w32.NOTIFYICONDATA{ + UID: s.uid, + HWnd: s.hwnd, + } + nid.CbSize = uint32(unsafe.Sizeof(nid)) + return nid +} + +func (s *windowsSystemTray) setIcon(icon []byte) { + var err error + // Destroy the previous light mode icon if it exists + if s.lightModeIcon != 0 { + w32.DestroyIcon(s.lightModeIcon) + } + s.lightModeIcon, err = w32.CreateSmallHIconFromImage(icon) + if err != nil { + panic(syscall.GetLastError()) + } + if s.darkModeIcon == 0 { + s.darkModeIcon = s.lightModeIcon + } + // Update the icon + s.updateIcon() +} + +func (s *windowsSystemTray) setDarkModeIcon(icon []byte) { + var err error + // Destroy the previous dark mode icon if it exists + if s.darkModeIcon != 0 { + w32.DestroyIcon(s.darkModeIcon) + } + s.darkModeIcon, err = w32.CreateSmallHIconFromImage(icon) + if err != nil { + panic(syscall.GetLastError()) + } + if s.lightModeIcon == 0 { + s.lightModeIcon = s.darkModeIcon + } + // Update the icon + s.updateIcon() +} + +func newSystemTrayImpl(parent *SystemTray) systemTrayImpl { + return &windowsSystemTray{ + parent: parent, + } +} + +func (s *windowsSystemTray) wndProc(msg uint32, wParam, lParam uintptr) uintptr { + switch msg { + case WM_USER_SYSTRAY: + msg := lParam & 0xffff + switch msg { + case w32.WM_LBUTTONUP: + if s.parent.clickHandler != nil { + s.parent.clickHandler() + } + case w32.WM_RBUTTONUP: + if s.parent.rightClickHandler != nil { + s.parent.rightClickHandler() + } + case w32.WM_LBUTTONDBLCLK: + if s.parent.doubleClickHandler != nil { + s.parent.doubleClickHandler() + } + case w32.WM_RBUTTONDBLCLK: + if s.parent.rightDoubleClickHandler != nil { + s.parent.rightDoubleClickHandler() + } + case 0x0406: + if s.parent.mouseEnterHandler != nil { + s.parent.mouseEnterHandler() + } + case 0x0407: + if s.parent.mouseLeaveHandler != nil { + s.parent.mouseLeaveHandler() + } + } + // println(w32.WMMessageToString(msg)) + + // Menu processing + case w32.WM_COMMAND: + cmdMsgID := int(wParam & 0xffff) + switch cmdMsgID { + default: + s.menu.ProcessCommand(cmdMsgID) + } + default: + // msg := int(wParam & 0xffff) + // println(w32.WMMessageToString(uintptr(msg))) + } + + return w32.DefWindowProc(s.hwnd, msg, wParam, lParam) +} + +func (s *windowsSystemTray) updateMenu(menu *Menu) { + s.menu = NewPopupMenu(s.hwnd, menu) + s.menu.onMenuOpen = s.parent.onMenuOpen + s.menu.onMenuClose = s.parent.onMenuClose + s.menu.Update() +} + +// Based on the idea from https://github.com/wailsapp/wails/issues/3487#issuecomment-2633242304 +func (s *windowsSystemTray) setTooltip(tooltip string) { + // Ensure the tooltip length is within the limit (64 characters for szTip) + if len(tooltip) > 64 { + tooltip = tooltip[:64] + } + + // Create a new NOTIFYICONDATA structure + nid := s.newNotifyIconData() + nid.UFlags = w32.NIF_TIP + tooltipUTF16, err := w32.StringToUTF16(tooltip) + if err != nil { + return + } + + copy(nid.SzTip[:], tooltipUTF16) + + // Modify the tray icon with the new tooltip + if !w32.ShellNotifyIcon(w32.NIM_MODIFY, &nid) { + return + } + nid.UVersion = 3 // Version 4 does not suport + if !w32.ShellNotifyIcon(w32.NIM_SETVERSION, &nid) { + return + } +} + +// ---- Unsupported ---- +func (s *windowsSystemTray) setLabel(label string) {} + +func (s *windowsSystemTray) setTemplateIcon(_ []byte) { + // Unsupported - do nothing +} + +func (s *windowsSystemTray) setIconPosition(position IconPosition) { + // Unsupported - do nothing +} + +func (s *windowsSystemTray) destroy() { + // Remove and delete the system tray + getNativeApplication().unregisterSystemTray(s) + if s.menu != nil { + s.menu.Destroy() + } + w32.DestroyWindow(s.hwnd) + // destroy the notification icon + nid := s.newNotifyIconData() + if !w32.ShellNotifyIcon(w32.NIM_DELETE, &nid) { + globalApplication.debug(syscall.GetLastError().Error()) + } + + // Clean up icon handles + if s.lightModeIcon != 0 { + w32.DestroyIcon(s.lightModeIcon) + s.lightModeIcon = 0 + } + if s.darkModeIcon != 0 && s.darkModeIcon != s.lightModeIcon { + w32.DestroyIcon(s.darkModeIcon) + s.darkModeIcon = 0 + } + s.currentIcon = 0 +} + +func (s *windowsSystemTray) Show() { + // No-op +} + +func (s *windowsSystemTray) Hide() { + // No-op +} + +func (s *windowsSystemTray) reshow() { + // Add icons back to systray + nid := w32.NOTIFYICONDATA{ + HWnd: s.hwnd, + UID: uint32(s.parent.id), + UFlags: w32.NIF_ICON | w32.NIF_MESSAGE, + HIcon: s.currentIcon, + UCallbackMessage: WM_USER_SYSTRAY, + } + nid.CbSize = uint32(unsafe.Sizeof(nid)) + // Show the icon + if !w32.ShellNotifyIcon(w32.NIM_ADD, &nid) { + panic(syscall.GetLastError()) + } +} diff --git a/v3/pkg/application/urlvalidator.go b/v3/pkg/application/urlvalidator.go new file mode 100644 index 000000000..f9eecf0bb --- /dev/null +++ b/v3/pkg/application/urlvalidator.go @@ -0,0 +1,49 @@ +package application + +import ( + "errors" + "fmt" + "net/url" + "regexp" + "strings" +) + +func ValidateAndSanitizeURL(rawURL string) (string, error) { + if strings.Contains(rawURL, "\x00") { + return "", errors.New("null bytes not allowed in URL") + } + + for i, r := range rawURL { + if r < 32 && r != 9 { + return "", fmt.Errorf("control character at position %d not allowed", i) + } + } + + shellDangerous := `[;|` + "`" + `$\\<>*{}\[\]()~! \t\n\r]` + if matched, _ := regexp.MatchString(shellDangerous, rawURL); matched { + return "", errors.New("shell metacharacters not allowed") + } + + unicodeDangerous := "[\u0000-\u001F\u007F\u00A0\u1680\u2000-\u200F\u2028-\u202F\u205F\u3000\uFEFF\u200B-\u200D\u2060\u2061\u2062\u2063\u2064\u206A-\u206F\uFFF0-\uFFFF]" + if matched, _ := regexp.MatchString(unicodeDangerous, rawURL); matched { + return "", errors.New("dangerous unicode characters not allowed") + } + + parsedURL, err := url.Parse(rawURL) + if err != nil { + return "", fmt.Errorf("invalid URL format: %v", err) + } + + scheme := strings.ToLower(parsedURL.Scheme) + + if scheme == "javascript" || scheme == "data" || scheme == "file" || scheme == "ftp" || scheme == "" { + return "", errors.New("scheme not allowed") + } + + if (scheme == "http" || scheme == "https") && parsedURL.Host == "" { + return "", fmt.Errorf("missing host for %s URL", scheme) + } + + sanitizedURL := parsedURL.String() + return sanitizedURL, nil +} diff --git a/v3/pkg/application/urlvalidator_test.go b/v3/pkg/application/urlvalidator_test.go new file mode 100644 index 000000000..803098899 --- /dev/null +++ b/v3/pkg/application/urlvalidator_test.go @@ -0,0 +1,262 @@ +package application_test + +import ( + "strings" + "testing" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func TestValidateURL(t *testing.T) { + testCases := []struct { + name string + url string + shouldErr bool + errMsg string + expected string + }{ + { + name: "valid https URL", + url: "https://www.example.com", + shouldErr: false, + expected: "https://www.example.com", + }, + { + name: "valid http URL", + url: "http://example.com", + shouldErr: false, + expected: "http://example.com", + }, + { + name: "URL with query parameters", + url: "https://example.com/search?q=cats&dogs", + shouldErr: false, + expected: "https://example.com/search?q=cats&dogs", + }, + { + name: "URL with port", + url: "https://example.com:8080/path", + shouldErr: false, + expected: "https://example.com:8080/path", + }, + { + name: "URL with fragment", + url: "https://example.com/page#section", + shouldErr: false, + expected: "https://example.com/page#section", + }, + { + name: "urlencode params", + url: "http://google.com/ ----browser-subprocess-path=C:\\\\Users\\\\Public\\\\test.bat", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "javascript scheme", + url: "javascript:alert('XSS')", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "data scheme", + url: "data:text/html,", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "file scheme", + url: "file:///etc/passwd", + shouldErr: true, + errMsg: "scheme not allowed", + }, + { + name: "ftp scheme", + url: "ftp://ftp.example.com/file", + shouldErr: true, + errMsg: "scheme not allowed", + }, + { + name: "missing scheme", + url: "example.com", + shouldErr: true, + errMsg: "scheme not allowed", + }, + { + name: "empty string", + url: "", + shouldErr: true, + errMsg: "scheme not allowed", + }, + { + name: "null byte in URL", + url: "https://example.com\x00/malicious", + shouldErr: true, + errMsg: "null bytes not allowed", + }, + { + name: "control character", + url: "https://example.com\x01", + shouldErr: true, + errMsg: "control character", + }, + { + name: "shell injection with semicolon", + url: "https://example.com/;rm -rf /", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "shell injection with pipe", + url: "https://example.com/|cat /etc/passwd", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "shell injection with backtick", + url: "https://example.com/`whoami`", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "shell injection with dollar", + url: "https://example.com/$(whoami)", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "unicode null", + url: "https://example.com/\u0000", + shouldErr: true, + errMsg: "null bytes not allowed", + }, + { + name: "missing host for http", + url: "http:///path", + shouldErr: true, + errMsg: "missing host", + }, + { + name: "missing host for https", + url: "https:///path", + shouldErr: true, + errMsg: "missing host", + }, + { + name: "URL with newline", + url: "https://example.com/path\n/newline", + shouldErr: true, + errMsg: "control character", + }, + { + name: "URL with carriage return", + url: "https://example.com/path\r/return", + shouldErr: true, + errMsg: "control character", + }, + { + name: "URL with tab", + url: "https://example.com/path\t/tab", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "URL with space in path", + url: "https://example.com/path with spaces", + shouldErr: true, + errMsg: "shell metacharacters", + }, + { + name: "URL with angle brackets", + url: "https://example.com/ + + \ No newline at end of file diff --git a/v3/tests/window-visibility-test/go.mod b/v3/tests/window-visibility-test/go.mod new file mode 100644 index 000000000..04d244468 --- /dev/null +++ b/v3/tests/window-visibility-test/go.mod @@ -0,0 +1,55 @@ +module window-visibility-test + +go 1.24.0 + +toolchain go1.24.4 + +replace github.com/wailsapp/wails/v3 => ../../ + +require github.com/wailsapp/wails/v3 v3.0.0-alpha.0 + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/bep/debounce v1.2.1 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect + github.com/leaanthony/u v1.1.1 // indirect + github.com/lmittmann/tint v1.0.7 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pjbgf/sha1cd v0.3.2 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) + +// Add any other dependencies that might be needed +// These will be resolved when the user runs go mod tidy diff --git a/v3/tests/window-visibility-test/go.sum b/v3/tests/window-visibility-test/go.sum new file mode 100644 index 000000000..cbadfe003 --- /dev/null +++ b/v3/tests/window-visibility-test/go.sum @@ -0,0 +1,146 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= +github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +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.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/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/v3/tests/window-visibility-test/main.go b/v3/tests/window-visibility-test/main.go new file mode 100644 index 000000000..247c1035c --- /dev/null +++ b/v3/tests/window-visibility-test/main.go @@ -0,0 +1,308 @@ +package main + +import ( + "embed" + "fmt" + "log" + "time" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// WindowTestService provides methods for testing window visibility scenarios +type WindowTestService struct { + app *application.App +} + +// NewWindowTestService creates a new window test service +func NewWindowTestService() *WindowTestService { + return &WindowTestService{} +} + +// SetApp sets the application reference (internal method, not exposed to frontend) +func (w *WindowTestService) setApp(app *application.App) { + w.app = app +} + +// CreateNormalWindow creates a standard window - should show immediately +func (w *WindowTestService) CreateNormalWindow() string { + log.Println("Creating normal window...") + + w.app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Normal Window - Should Show Immediately", + Width: 600, + Height: 400, + X: 100, + Y: 100, + HTML: "Normal Window

✅ Normal Window

This window should have appeared immediately after clicking the button.

Timestamp: " + time.Now().Format("15:04:05") + "

", + }) + + return "Normal window created" +} + +// CreateDelayedContentWindow creates a window with delayed content to test navigation timing +func (w *WindowTestService) CreateDelayedContentWindow() string { + log.Println("Creating delayed content window...") + + // Use HTML that will take time to load (simulates heavy Vue app) + delayedHTML := ` + + + Delayed Content + + + +

⏳ Delayed Content Window

+

This window tests navigation completion timing.

+
+

Loading... (simulates heavy content)

+ +

Window container should be visible immediately, even during load.

+ + ` + + w.app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Delayed Content Window - Test Navigation Timing", + Width: 600, + Height: 400, + X: 150, + Y: 150, + HTML: delayedHTML, + }) + + return "Delayed content window created" +} + +// CreateHiddenThenShowWindow creates a hidden window then shows it after delay +func (w *WindowTestService) CreateHiddenThenShowWindow() string { + log.Println("Creating hidden then show window...") + + window := w.app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Hidden Then Show Window - Test Show() Robustness", + Width: 600, + Height: 400, + X: 200, + Y: 200, + HTML: "Hidden Then Show

🔄 Hidden Then Show Window

This window was created hidden and then shown after 2 seconds.

Should test the robustness of the show() method.

Created at: " + time.Now().Format("15:04:05") + "

", + Hidden: true, // Start hidden + }) + + // Show after 2 seconds to test delayed showing + go func() { + time.Sleep(2 * time.Second) + log.Println("Showing previously hidden window...") + window.Show() + }() + + return "Hidden window created, will show in 2 seconds" +} + +// CreateMultipleWindows creates multiple windows simultaneously to test performance +func (w *WindowTestService) CreateMultipleWindows() string { + log.Println("Creating multiple windows...") + + for i := 0; i < 3; i++ { + bgColors := []string{"#ff9a9e,#fecfef", "#a18cd1,#fbc2eb", "#fad0c4,#ffd1ff"} + content := fmt.Sprintf(` + + + Batch Window %d + + + +

🔢 Batch Window %d

+

Part of multiple windows stress test

+

All windows should appear quickly and simultaneously

+

Created at: %s

+ + `, i+1, bgColors[i], i+1, time.Now().Format("15:04:05")) + + w.app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: fmt.Sprintf("Batch Window %d - Stress Test", i+1), + Width: 400, + Height: 300, + X: 250 + (i * 50), + Y: 250 + (i * 50), + HTML: content, + }) + } + + return "Created 3 windows simultaneously" +} + +// CreateEfficiencyModeTestWindow creates a window designed to trigger efficiency mode issues +func (w *WindowTestService) CreateEfficiencyModeTestWindow() string { + log.Println("Creating efficiency mode test window...") + + // Create content that might trigger efficiency mode or WebView2 delays + heavyHTML := ` + + + Efficiency Mode Test + + + +

⚡ Efficiency Mode Test Window

+

This window tests the fix for Windows 10 Pro efficiency mode issue #2861

+ +
+

Window Container Status

+

✅ Window container is visible (this text proves it)

+
+ +
+

WebView2 Status

+

⏳ WebView2 navigation in progress...

+ +
+ +
+

Heavy Content (simulates Vue.js app)

+
+ Loading heavy content... +
+
+ + + + ` + + w.app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Efficiency Mode Test - Issue #2861 Reproduction", + Width: 700, + Height: 500, + X: 300, + Y: 300, + HTML: heavyHTML, + }) + + return "Efficiency mode test window created" +} + +// GetWindowCount returns the current number of windows +func (w *WindowTestService) GetWindowCount() int { + // This would need to be implemented based on the app's window tracking + // For now, return a placeholder + return 1 // Main window +} + +//go:embed assets/* +var assets embed.FS + +func main() { + // Create the service + service := NewWindowTestService() + + // Create application with menu + app := application.New(application.Options{ + Name: "Window Visibility Test", + Description: "Test application for window visibility robustness (Issue #2861)", + Services: []application.Service{ + application.NewService(service), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + // Set app reference in service + service.setApp(app) + + // Create application menu + menu := app.NewMenu() + + // File menu + fileMenu := menu.AddSubmenu("File") + fileMenu.Add("New Normal Window").OnClick(func(ctx *application.Context) { + service.CreateNormalWindow() + }) + fileMenu.Add("New Delayed Content Window").OnClick(func(ctx *application.Context) { + service.CreateDelayedContentWindow() + }) + fileMenu.AddSeparator() + fileMenu.Add("Quit").OnClick(func(ctx *application.Context) { + app.Quit() + }) + + // Test menu + testMenu := menu.AddSubmenu("Tests") + testMenu.Add("Hidden Then Show Window").OnClick(func(ctx *application.Context) { + service.CreateHiddenThenShowWindow() + }) + testMenu.Add("Multiple Windows Stress Test").OnClick(func(ctx *application.Context) { + service.CreateMultipleWindows() + }) + testMenu.Add("Efficiency Mode Test").OnClick(func(ctx *application.Context) { + service.CreateEfficiencyModeTestWindow() + }) + + // Help menu + helpMenu := menu.AddSubmenu("Help") + helpMenu.Add("About").OnClick(func(ctx *application.Context) { + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "About Window Visibility Test", + Width: 500, + Height: 400, + X: 400, + Y: 300, + HTML: "About

Window Visibility Test

This application tests the fixes for Wails v3 issue #2861

Windows 10 Pro Efficiency Mode Fix

Tests window container vs WebView content visibility


Created for testing robust window visibility patterns

", + }) + }) + + app.Menu.Set(menu) + + // Create main window + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window Visibility Test - Issue #2861", + Width: 800, + Height: 600, + X: 50, + Y: 50, + URL: "/index.html", + }) + + err := app.Run() + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/wep/README.md b/v3/wep/README.md new file mode 100644 index 000000000..63ea57c98 --- /dev/null +++ b/v3/wep/README.md @@ -0,0 +1,69 @@ +# Wails Enhancement Proposal (WEP) Process + +## Introduction + +Welcome to the Wails Enhancement Proposal (WEP) process. This guide outlines the steps for proposing, discussing, and implementing feature enhancements in Wails. The process is divided into two main parts: + +1. **Submission of Proposal**: This part involves documenting your idea, submitting it for review, and discussing it with the community to gather feedback and refine the proposal. +2. **Implementation of Proposal**: Once a proposal is accepted, the implementation phase begins. This involves developing the feature, submitting a PR for the implementation, and iterating based on feedback until the feature is merged and documented. + +Following this structured approach ensures transparency, community involvement, and efficient enhancement of the Wails project. + +**NOTE**: This process is for proposing new functionality. For bug fixes, documentation improvements, and other minor changes, please follow the standard PR process. + +## Submission of Proposal + +### 1. Idea Initiation + +- **Document Your Idea**: + - Create a new directory: `v3/wep/proposals/` with the name of your proposal. + - Copy the WEP template located in `v3/wep/WEP_TEMPLATE.md` into `v3/wep/proposals//proposal.md`. + - Include any additional resources (images, diagrams, etc.) in the proposal directory. + - Fill in the template with the details of your proposal. Do not remove any sections. + +### 2. Submit Proposal + +- **Open a DRAFT PR**: + - Submit a DRAFT Pull Request (PR) for the proposal with the title `[WEP] `. + - It should only contain the proposal file and any additional resources (images, diagrams, etc.). + - Add a summary of the proposal in the PR description. + +### 3. Community Discussion + +- **Share Your Proposal**: Present your proposal to the Wails community. Try to get support for the proposal to increase the chances of acceptance. If you are on the discord server, create a post in the [`#enhancement-proposals`](https://discord.gg/TA8kbQds95) channel. +- **Gather Feedback**: Refine your proposal based on community input. All feedback should be added as comments in the PR. +- **Show Support**: Agreement with the proposal should be indicated by adding a thumbs-up emoji to the PR. The more thumbs-up emojis, the more likely the proposal will be accepted. +- **Iterate**: Make changes to the proposal based on feedback. +- **Agree on an Implementor**: To avoid stagnant proposals, we require someone agree to implement it. This could be the proposer. +- **Ready for Review**: Once the proposal is ready for review, change the PR status to `Ready for Review`. + +A minimum of 2 weeks should be given for community feedback and discussion. + +### 4. Final Decision + +- **Decision**: The Wails maintainers will make a final decision on the proposal based on community feedback and the proposal's merits. + - If accepted, the proposal will be assigned a WEP number and the PR merged. + - If rejected, the reasons will be provided in the PR comments. + +*NOTE*: If a proposal has not met the required support or has been inactive for more than a month, it may be closed. + +## Implementation of Proposal + +Once a proposal has been accepted and an implementation plan has been decided, the focus shifts to bringing the feature to life. This phase encompasses the actual development, review, and integration of the new feature. Here are the steps involved in the implementation process: + +### 1. Develop the Feature + +- **Follow Standards**: Implement the feature following Wails coding standards. +- **Document the Feature**: Ensure the feature is well-documented during the development process. +- **Submit a PR**: Once implemented, submit a PR for the feature. + +### 2. Feedback and Iteration + +- **Gather Feedback**: Collect feedback from the community. +- **Iterate**: Make improvements based on feedback. + +### 3. Merging + +- **Review of PR**: Address any review comments. +- **Merge**: The PR will be merged after satisfactory review. +The WEP process ensures structured and collaborative enhancement of Wails. Adhere to this guide to contribute effectively to the project's growth. \ No newline at end of file diff --git a/v3/wep/WEP_TEMPLATE.md b/v3/wep/WEP_TEMPLATE.md new file mode 100644 index 000000000..c822c2361 --- /dev/null +++ b/v3/wep/WEP_TEMPLATE.md @@ -0,0 +1,50 @@ +# Wails Enhancement Proposal (WEP) + +## Title + +**Author**: [Your Name] +**Created**: [YYYY-MM-DD] + +## Summary + +Provide a concise summary of the proposal. + +## Motivation + +Explain the problem this proposal aims to solve and why it is necessary. + +## Detailed Design + +Provide a detailed description of the proposed feature, including: + +- Technical details +- Implementation steps +- Potential impact on existing functionality + +## Pros/Cons + +List the pros and cons of the proposed solution. + +## Alternatives Considered + +Discuss alternative solutions or approaches that were considered. + +## Backwards Compatibility + +Address any potential backward compatibility issues. + +## Test Plan + +Outline a testing strategy to ensure the feature works as expected. + +## Reference Implementation + +If applicable, include a link to a reference implementation or prototype. + +## Maintenance Plan + +Describe how the feature will be maintained and supported in the long term. + +## Conclusion + +Summarize the benefits of the proposal and any final considerations. diff --git a/v3/wep/proposals/.gitkeep b/v3/wep/proposals/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/v3/wep/proposals/titlebar-buttons/proposal.md b/v3/wep/proposals/titlebar-buttons/proposal.md new file mode 100644 index 000000000..8bd6b8c4f --- /dev/null +++ b/v3/wep/proposals/titlebar-buttons/proposal.md @@ -0,0 +1,137 @@ +# Wails Enhancement Proposal (WEP) + +## Customising Window Controls in Wails + +**Author**: Lea Anthony +**Created**: 2024-05-20 + +## Summary + +This is a proposal for an API to control the appearance and functionality of window controls in Wails. +This will only be available on Windows and macOS. + +## Motivation + +We currently do not fully support the ability to customise window controls. + +## Detailed Design + +### Controlling Button State + +1. A new enum will be added: + +```go + type ButtonState int + + const ( + ButtonEnabled ButtonState = 0 + ButtonDisabled ButtonState = 1 + ButtonHidden ButtonState = 2 + ) +``` + +2. These options will be added to the `WebviewWindowOptions` option struct: + +```go + MinimiseButtonState ButtonState + MaximiseButtonState ButtonState + CloseButtonState ButtonState +``` + +3. These options will be removed from the current Windows/Mac options: + +- DisableMinimiseButton +- DisableMaximiseButton +- DisableCloseButton + +4. These methods will be added to the `Window` interface: + +```go + SetMinimizeButtonState(state ButtonState) + SetMaximizeButtonState(state ButtonState) + SetCloseButtonState(state ButtonState) +``` + +The settings translate to the following functionality on each platform: + +| | Windows | Mac | +|-----------------------|------------------------|------------------------| +| Disable Min/Max/Close | Disables Min/Max/Close | Disables Min/Max/Close | +| Hide Min | Disables Min | Hides Min button | +| Hide Max | Disables Max | Hides Max button | +| Hide Close | Hides all controls | Hides Close | + +Note: On Windows, it is not possible to hide the Min/Max buttons individually. +However, disabling both will hide both of the controls and only show the +close button. + +### Controlling Window Style (Windows) + +As Windows currently does not have much in the way of controlling the style of the +titlebar, a new option will be added to the `WebviewWindowOptions` option struct: + +```go + ExStyle int +``` + +If this is set, then the new Window will use the style specified in the `ExStyle` field. + +Example: +```go +package main + +import ( + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/w32" +) + +func main() { + app := application.New(application.Options{ + Name: "My Application", + }) + + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + Windows: application.WindowsWindow{ + ExStyle: w32.WS_EX_TOOLWINDOW | w32.WS_EX_NOREDIRECTIONBITMAP | w32.WS_EX_TOPMOST, + }, + }) + + app.Run() +} +``` + +## Pros/Cons + +### Pros + +- We bring much needed customisation capabilities to both macOS and Windows + +### Cons + +- Windows works slightly different to macOS +- No Linux support (doesn't look like it's possible regardless of the solution) + +## Alternatives Considered + +The alternative is to draw your own titlebar, but this is a lot of work and often doesn't look good. + +## Backwards Compatibility + +This is not backwards compatible as we remove the old "disable button" options. + +## Test Plan + +As part of the implementation, the window example will be updated to test the functionality. + +## Reference Implementation + +There is a reference implementation as part of this proposal. + +## Maintenance Plan + +This feature will be maintained and supported by the Wails developers. + +## Conclusion + +This API would be a leap forward in giving developers greater control over their application window appearances. + diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/version-v2.6.0/gettingstarted/_category_.json b/website/i18n/ja/docusaurus-plugin-content-docs/version-v2.6.0/gettingstarted/_category_.json deleted file mode 100644 index 597b920df..000000000 --- a/website/i18n/ja/docusaurus-plugin-content-docs/version-v2.6.0/gettingstarted/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Getting Started", - "position": 10 -} diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index d1b405813..c5f51220e 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Fixed locking issue on Windows when multiselect dialog returns an error. Fixed in [PR](https://github.com/wailsapp/wails/pull/4156) by @johannes-luebke + ## v2.9.1 - 2024-06-18 ### Fixed